- 投稿日:2020-11-17T23:48:59+09:00
いらないコメントを一括削除する方法
これを覚えると時間の節約になるのでおすすめです。
前提として、環境はMacでエディターはVSCodeです。(Atomでや他のエディターでも同様のコマンドで可能かと思います)
Railsでテストアプリを作っている時などによく出る長文のコメント結構邪魔ですよね。
そんな時は
- command+Fで任意のページで検索して
# .*\n
- このコマンドを打ちますと(今回はVScode使ってます)
- 結果はありませんとなってしまいましたが、正規表現を表す
- 全て置換すると
コメントが一気に消えます。
ちなみにバックスラッシュの打ち方は日本語キーボードですと
Option + ¥ = \です。
是非試してみてくださいね。
- 投稿日:2020-11-17T23:34:08+09:00
LINEみたいに自分のメッセージと自分以外のメッセージの表示場所を変えたい
はじめに
現在作成中のチャットアプリのメッセージ画面をLINEのように自分の送ったメッセージは右、自分以外のメッセージは左に表示させよと一日苦戦した。ググってもあまり記事が見つからなかったので、ここに記録しておく。
ポイントとして使ったもの
- renderメソッド
- if文
- deviseのメソッド
コントローラー
messages_controller.rbdef index @class_room = ClassRoom.find(params[:class_room_id]) @messages = @class_room.messages.includes(:user) end
@class_room
にチャットルームをparamsから呼び出し代入
@messages
にそのチャットルームのメッセージを全て入れる。
.includes(:user)
はN+1問題を解消するため。ビュー
※divタブはコードが読みにくくなるため、ここでは割愛。
<% if message.user.id == current_user.id %> <%= l message.created_at %> <%= message.content %> <% else %> <%= message.user.last_name %> <%= l message.created_at %> <%= message.content %> <% end %>if文で、メッセージのユーザーIDと現在のログインしているユーザーが同じか照会し、同じであれば(つまり、自分の送ったメッセージ)上の処理が行われ、違えば、下の処理が行われる。
自分のメッセージに送り主の表示は必要ないので、上の処理にmessage.user.last_name
がない。その他
CSSを整える。
終わりに
他のやり方として、コントローラーの時点で、自分のメッセージと自分以外のメッセージを別の変数に入れる方法も考えられる。
- 投稿日:2020-11-17T22:37:03+09:00
【初心者向け】Rails6で作られたWebアプリをCircleCIを使いAWS ECR・ECSへ自動デプロイする方法②-1 インフラ構築編【コンテナデプロイ】
記事の続きをご覧いただきありがとうございます。
前回の下準備編の続きになります。今回はインフラ構築編②-1です。
タイトル ① 下準備編 ②-1 インフラ構築編 ←今ここ! ②-2 インフラ構築編(執筆中) 自動デプロイ編(執筆中) 少し長いので覚悟してください!笑
それでは、早速やってきましょう!
インフラ構築編②-1
最初に流れを説明します。ここでやることは主に2つだけですが、色々と設定する項目があります。地道にやっていきましょう。
- クラスターの作成
- RDSの設置
このような構成になってます。
クラスターの作成
クラスターの作成をawsコマンドを使って作成します。クラスターというのはコンテナインスタンスの集合体の名称です。
(クラスターという単語は、今年話題になったので日本語の意味からなんとなく察しが付いた方も多いと思います)ざっくりいうと、このクラスターの中にRailsとNginxのDockerコンテナ(このDockerコンテナの集まりをServiceと呼びます)が配置されるといった感じです。
以下のコマンドを順番に実行して作成していきます。
$ ecs-cli configure profile --profile-name 任意のプロフィール名 --access-key アクセスキー --secret-key シークレットアクセスキー $ ecs-cli configure --cluster 任意のクラスター名 --default-launch-type EC2 --config-name 任意の設定名 --region ap-northeast-1 $ ecs-cli up --keypair キーペア名 --capability-iam --size 2 --instance-type t2.small --cluster-config 任意の設定名 --ecs-profile 任意のプロフィール名私の場合は、アプリ名(protuku-app)を名前にしたので、
$ ecs-cli configure profile --profile-name protuku-app --access-key xxxxxxxxxxxxxxxx --secret-key xxxxxxxxxxxxxx $ ecs-cli configure --cluster protuku-app-cluster --default-launch-type EC2 --config-name protuku-app-cluster --region ap-northeast-1 $ ecs-cli up --keypair protuku-app --capability-iam --size 2 --instance-type t2.small --cluster-config protuku-app-cluster --ecs-profile protuku-appのような感じでコマンドを実行しました。
また、私の場合、最後のecs-cli upコマンドを実行したとき
time="2020-10-22T20:41:46+09:00" level=fatal msg="Error executing 'up': describe instance type offerings: AuthFailure: AWS was not able to validate the provided access credentials\n\tstatus code: 401, request id: xxxxxxxxxのようなエラーが出ました。もし、このようなエラーが起きたら、キーペアやアクセスキーの設定が間違っている可能性があります。私は、IAMの作成からやり直したらうまくいったのでこの辺の設定が間違っていた可能性があります。
また、ローカルのPCの時刻がずれていてもこのようなエラーが起きる可能性がありますので、PCの時刻がずれていないかどうか確認してください。
詳しくはこちらの記事を参考にしてみるといいかもしれません。最後のecs-cli upコマンドの実行に成功すると次のように、クラスター用のVPC、サブネット、セキュリティグループなどが作成されます!
$ ecs-cli up --keypair protuku-app --capability-iam --size 2 --instance-type t2.small --cluster-config protuku-app-cluster --ecs-profile protuku-app INFO[0000] Saved ECS CLI cluster configuration protuku-app-cluster-4 INFO[0000] Using recommended Amazon Linux 2 AMI with ECS Agent 1.46.0 and Docker version 19.03.6-ce INFO[0000] Created cluster cluster=protuku-app-cluster region=ap-northeast-1 INFO[0001] Waiting for your cluster resources to be created... INFO[0001] Cloudformation stack status stackStatus=CREATE_IN_PROGRESS INFO[0062] Cloudformation stack status stackStatus=CREATE_IN_PROGRESS INFO[0122] Cloudformation stack status stackStatus=CREATE_IN_PROGRESS VPC created: vpc-xxxxxxxxxxxxxxxxxx Security Group created: sg-xxxxxxxxxxxxxxxxxx Subnet created: subnet-xxxxxxxxxxxxxxxxxx Subnet created: subnet-xxxxxxxxxxxxxxxxxx Cluster creation succeeded.ServicesからElastic Container Service(ECS)を選択し、Clusterをクリックすると、以下のようにクラスターが生成されているのが確認できると思います。(こちらはデプロイ後の画面なのでService、Taskが1になってますが、実際はまだ作成していないので0になっているはずです。)
RDSの作成
次にRDSを作成していきます。DBサーバーにはMySQLを利用します。
RDSというのは、 AWSのフルマネージドなリレーショナルデータベースのサービスになります。バックアップ、DBのアップデート、スケーリングなどをAWSがすべて自動でやってくれるので、よりコアな開発に集中することができる!といったものです。RDSにはインターネット上からアクセスできないようにしたいのでプライベートサブネットに配置します。また、冗長化のために複数のアベイラビリティゾーンにRDSを設置していきます。
まず、事前準備として、RDS用のプライベートサブネットを2つ(リージョンが1aと1cのものでいいと思います)と、DBパラメータグループ、DBオプショングループを作成していきます。
RDS用のプライベートサブネットの作成
他記事で恐縮ですが、プライベートサブネットの作成に関してはこちらの記事が大変わかりやすいので、こちらを見て作成してみてください。VPCはすでにあるのでサブネットの作成だけでOKです。
一番近い東京リージョンであるap-northeast-1aと1cのふたつのプライベートサブネットを作りこのサブネットにRDSを設置していきます。RDS用のサブネットグループを作成する
サブネットグループというのは、VPC内にあるサブネットを複数指定して、RDSインスタンスが起動するサブネットを指定した設定のことです。マルチAZを実現させるために、この設定をする必要があります。
まず、ServicesからRDSのコンソールへいき、Subnet groupsを選択し、Create DB Subnet Groupをクリックします。
以下のように任意の名前を入れます。VPCには作成したVPCを選択します。
次に最初に作った2つのプライベートサブネットを追加していきます。
Avalilability Zonesは1aと1c選択し、作成したサブネットを2つ選択して、Createをクリックして作成完了です。
DBパラメータグループの作成
RDSではDBの設定ファイルを直接編集するといったことができないので、代わりにパラメータグループというのを使って設定値を編集することができます。
デフォルトでパラメータグループは作成されるのですが、こちらは設定値を変えることができないので、自分で作成する必要があります。RDSのコンソールからParameter groupsをクリックし、Create parameter groupをクリック
group familyにmysql5.7を選択し、任意の名前を入れてCreateして完了です。
DBオプショングループの作成
オプショングループはDBの機能的な部分を設定します。プラグインを導入したいとかそういったときに利用します。
こちらもデフォルトで設定されるのですが、デフォルトのものを編集するのではなく、自分で作成したものを編集するのがセオリーのようです。後々変更したいといったときのために作成しておきます。RDSのコンソールからOption groupsを選択し、Create groupをクリック
こちらも任意の名前を入力して、mysqlの5.7を選択し、createをクリックして作成完了です。
RDSインスタンスの設置
さて、下準備が整ったのでやっとインスタンスを作成できます!
RDSのコンソールからDatabaseを選択し、Create databaseをクリック。
スタンダードを選択し、DBはMySQLを選択
今回は無料タイプを選択します。
次に、任意のDBインスタンス名を入力します。Credentials Settingsをクリックし、DBに接続するときの任意のユーザー名とパスワードを設定します。
こちらはなるべく安く済ませたいので、バースト可能クラスを選び、t3.microを選択します。
マルチAZの設定は今回は無しにします。作成すると自動でマルチAZにできるのですが、料金がかかってしまうため設定しません。デフォルトだと作成するにチェックが入っているので気を付けましょう。(英語版だけかも)
作成したVPCとサブネットグループを選択します。パブリックアクセスは無しを選択します。
ここで新規にRDSのセキュリティグループを作成します。任意の名前を入力し、アベイラビリティゾーンは1aを選択します。
ポート番号はデフォルトの3306のままいきます。
次に追加の接続設定を開き、database nameを入力します。
これは、Railsアプリのconfig/database.yml
のproduction:
にあるdatabaseの名前と同じ名前を入力しましょう。
たとえば私の場合は、webapp_productionと入力しました。
その後、上記で作成したパラメータグループとオプショングループを選択します。(画像は諸事情によりデフォルトのままになってますので気をつけてください。)
その他色々設定がありますが、他はデフォルトのままでとりあえずはOKだと思います!
設定があっていることを確認してCreateDatabaseをクリックしてRDSの作成は完了です。
RDSのセキュリティグループを設定する
現状のままではセキュリティグループが設定されていないので、DBにすべてのトラフィックを許可してしまってる状態です。これではセキュリティー上よろしくないので、DBにはサーバーからのSSH接続のみ許可するように設定していきます。
EC2のコンソールへいき、Security Groupsを選択し、Edit inbound rulesを選択します。
設定は
- タイプ: MYSQL/Aurora
- プロトコル: SSH
- ポート: 3306
- ソース: クラスターのセキュリティグループ(最初にawsコマンドを実行したとき自動で作られてるはずです)
設定を入力したら、Createをクリックします。これでセキュリティグループの作成は完了です。
セキュリティグループの作成が完了したら、実際にDBに接続できるか確認してみましょう。
サーバーにSSHログインして、以下のコマンドを実行します。サーバーへのSSHログインの方法は割愛します。
windowsならRLoginやPuttyなどを使ってログインできるのでググってみてください。[ec2-user@ip-xxxxxxxx ~]$ mysql -u 設定したユーザー名 -h RDSのエンドポイント -p以下のような画面になったら成功です。(mysql not found とかでてきたらmysqlをインストールする必要があります。ref: https://hacknote.jp/archives/51267/)
最後まで読んでいただきありがとうございます!
今回はここで一旦区切ります。次回はインフラ構築編②-2として、Webからのアクセスを負荷分散するために、ALB(アプリケーションロードバランサー)を作成する方法を書いていこうと思いますので、乞うご期待ください!
思い出しながら調べつつ記事を書いているので、ご指摘や、ご不明な点などあればコメントいただけますと嬉しいです!
- 投稿日:2020-11-17T21:58:10+09:00
未経験のおっさんがプログラミングを学ぶまで
はいどうも!
某プログラミングスクールに通い始めた37歳のおっさんです。
プログラミングは完全に未経験。
そんなおっさんが転職するまでの軌跡(奇跡?)をぼちぼち綴っていこうと思います。投稿は今日が初めてですがスクールは今日で9日目。
HTML&CSSをやってRubyに入り、Railsをやって試験を受けて、というところです。簡単に私の経歴を
↪️
地方出身、東京の文系私立大学卒、東京で就職し営業職(toC & toB)でマネジメント経験あり。
インディーズですが学生時代にやっていたバンドでCDを1枚だけ出しています。
キャンプとジム通い、音楽掘りが趣味。
ジャンルはRock、JazzからEDMまで幅広く聴きます。
今はちょっと難しくなってしまいましたが海外旅行もそこそこ行きました。
なぜプログラミングを始めたのか、どうして転職を決意したのかなどは後々ゆっくりとupしていこうと思います。
とりあえずまだ始めたばかりですが感想としては
「めちゃくちゃやることがいっぱいある」
「勉強すること、覚えることが今までやってきたことの比じゃないレベルで多い」
この二つですね。
日々思ったこと、できなかったこと、疑問点などなど
自分が見返した時に
「あ〜こんなことあったな」とか、
「こんなことで悩んでたんだな」とかとか、
あとはこれを読んだ他の方に少しでも役に立てればなと思います。そんな具合ですが本日は頭がパンクしているのでこれにて
- 投稿日:2020-11-17T21:50:48+09:00
Deviseでログイン機能
はじめに
今回はDeviseを使ってログイン機能を作成していきます。
またDeviseの日本語化やBootstrap4の適用までを行っていきます。準備
$ rails new devise-sample $ rails g controller homes indexconfig/routes.rbRails.application.routes.draw do root 'homes#index' endナビバーを追加し,レスポンシブ対応のためのmetaタグを追加
app/views/layouts/application.html.erb(略) <%= csrf_meta_tags %> <%= csp_meta_tag %> <meta name="viewport" content="width=device-width,initial-scale=1"> //追加 (略) <body> <%= render 'shared/header' %> //追加 <%= yield %> </body>app/views/homes/index.html.erb<%= render 'shared/flash_messages' %>新しくsharedフォルダと各renderファイルを作成
フラッシュメッセージ
app/views/shared/_flash_messages.html.erb<% flash.each do |msg_type, msg| %> <div class="alert alert-<%= msg_type %>" role="alert" id="alert"> <a href="#" class="close" data-dismiss="alert">×</a> <%= msg %> </div> <% end %>ヘッダー
app/views/shared/_header.html.erb<header> <nav class="navbar navbar-expand navbar-light"> <%= link_to "Deviseサンプル", root_path, class: 'navbar-brand' %> <div id="Navber"> <ul class="navbar-nav"> <% if user_signed_in? %> <li class="nav-item active"> <%= link_to 'アカウント編集', edit_user_registration_path, class: 'nav-link' %> </li> <li class="nav-item active"> <%= link_to 'ログアウト', destroy_user_session_path, method: :delete, class: 'nav-link' %> </li> <% else %> <li class="nav-item active"> <%= link_to "新規登録", new_user_registration_path, class: 'nav-link' %> </li> <li class="nav-item active"> <%= link_to "ログイン", new_user_session_path, class: 'nav-link' %> </li> <% end %> </ul> </div> </nav> </header>Gemの追加
Gemfileに以下を追加して
$ bundle install
# ログイン機能に必要なGem gem 'devise' # 日本語化に必要なGem gem 'rails-i18n', '~> 5.1' gem 'devise-i18n' # Bootstrapに必要なGem gem 'bootstrap', '~> 4.4.1' gem 'jquery-rails' gem 'devise-bootstrap-views', '~> 1.0'$ bundle installBootstrapの導入
application.css
の拡張子をscss
に変更
application.scss
から,*= require_tree .
と*= require_self
を削除
application.scss
に@import "bootstrap";
を追加スタイルも追加
app/assets/stylesheets/application.scss@import "bootstrap"; // ログイン画面 .container-login { @extend .container-fluid; max-width: 576px; padding: 2rem; } // 「ログインしました」などのフラッシュ用スタイル .alert-notice { @extend .alert-info; } .alert-alert { @extend .alert-danger; }application.jsに3つ追加
Bootstrapと依存関係をapplication.jsに追記する
app/assets/javascripts/application.js//= require jquery3 //= require popper //= require bootstrap-sprocketsDeviseの導入
Devise
をインストール(userの箇所は,任意のモデル名でOKです)$ rails g devise:install $ rails g devise user $ rails db:create db:migrate補足
- Bootstrapのtooltipsやpopoverはpopper.jsに依存している
- bootstrapの依存gemにpopper_jsが指定されているため新たにインストールは不要
- コンパイルを高速化したい場合はbootstrap-sprocketsの代わりにbootstrapを指定する問題がなければ,
$ rails s
の後,http://localhost:3000
からログインボタンを押せば,ログイン画面が表示されます。Deviseの日本語化
config/application.rbmodule AssociationTutorial class Application < Rails::Application # 以下を追加すれば日本語に config.i18n.default_locale = :ja # タイムゾーンも変更するなら,以下を追加 config.time_zone = 'Asia/Tokyo' end endサーバーを落として
$ rails s
で再起動すれば日本語に変更されます。日本語訳を変更
日本語訳を変更したい場合は,次のコマンドでconfig/locales/devise.views.ja.ymlを作成し,編集すればOKです。
$ rails g devise:i18n:locale ja例えばアカウント登録を新規登録に変更したい場合は,config/locals/devise.views.ja.ymlの該当文字を置換すればOKです。
パスワードを忘れましたか?も違和感がありますので,パスワードの再設定に置換するのがよいと思います
ログイン画面などの変更
まず,次のコマンドでビューファイルを作成します。
$ rails g devise:i18n:views $ rails g devise:views:bootstrap_templates -f【参考】それぞれのコマンドの最後に例えばuserをつけることで,usersディレクトリ内にファイルを作成することもできますが,その場合は,次の3つの作業を行わないと反映されません。
devise.views.ja.yml
30行目のdevise
をusers
に変更
config/initializers/devise.rbにあるconfig.scoped_views = false
のコメントアウトを外してtrue
に変更
サーバーを落として$ rails s
で再起動お好みで変えていく
例えば<div class="container-login"> # 元のプログラム </div>エラーメッセージの変更
エラーメッセージの表示がいまいちなので変更するためにオーバーライドします。
app/helpers/devise_helper.rb
ファイルを作成し、以下を記述。app/helpers/devise_helper.rbmodule DeviseHelper def bootstrap_devise_error_messages! return "" if resource.errors.empty? html = "" resource.errors.full_messages.each do |error_message| html += <<-EOF <div class="alert alert-danger alert-dismissible" role="alert"> <button type="button" class="close" data-dismiss="alert"> <span aria-hidden="true">×</span> <span class="sr-only">close</span> </button> #{error_message} </div> EOF end html.html_safe end endログイン画面にもエラーメッセージを追加
app/views/devise/sessions/new.html.erb<div class="container-login"> <h1><%= t('.sign_in') %></h1> <%= render 'shared/flash_messages' %> //追加 <%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %>
app/views/devise
ディレクトリ内のファイルのbtn btn-primary
をbtn btn-primary btn-block
に置換すればボタンの横幅が自然になります。バリデーション
バリデーションはフロント側にも簡単に入れられます。
f.email_field,f.password_field
にrequired: true
を入れることで空欄投稿できなくなります。- 新規登録(アカウント登録)画面では,例えば,
f.password_field
にrequired: true, minlength: @minimum_password_length, maxlength: '30'
を追加すれば,文字数のバリデーションを追加できます。app/views/devise/registrations/new.html.erb<div class="form-group"> <%= f.label :password %> - <%= f.password_field :password, autocomplete: 'current-password', - class: 'form-control' %> + <%= f.password_field :password, autocomplete: 'current-password', + class: 'form-control', + required: true, + minlength: @minimum_password_length, + maxlength: '30' %>
_links.html.erb
を編集して,一番下のリンクをボタンにしてみます。<hr class="border-dark my-5"> <div class="form-group"> <%- if controller_name != 'sessions' %> <%= link_to t(".sign_in"), new_session_path(resource_name), class: 'btn btn-info btn-block' %><br /> <% end -%> <%- if devise_mapping.registerable? && controller_name != 'registrations' %> <%= link_to t(".sign_up"), new_registration_path(resource_name), class: 'btn btn-info btn-block' %><br /> <% end -%> <%- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' %> <%= link_to t(".forgot_your_password"), new_password_path(resource_name), class: 'btn btn-secondary btn-block' %><br /> <% end -%> <%- if devise_mapping.confirmable? && controller_name != 'confirmations' %> <%= link_to t('.didn_t_receive_confirmation_instructions'), new_confirmation_path(resource_name), class: 'btn btn-secondary btn-block' %><br /> <% end -%> <%- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %> <%= link_to t('.didn_t_receive_unlock_instructions'), new_unlock_path(resource_name), class: 'btn btn-secondary btn-block' %><br /> <% end -%> <%- if devise_mapping.omniauthable? %> <%- resource_class.omniauth_providers.each do |provider| %> <%= link_to t('.sign_in_with_provider', provider: OmniAuth::Utils.camelize(provider)), omniauth_authorize_path(resource_name, provider), class: 'btn btn-info btn-block' %><br /> <% end -%> <% end -%> </div>さらに,リンクのログイン,新規登録(アカウント登録)を次のように変更してみます
# 上2つを次に置き換え <%- if controller_name != 'sessions' %> <%= link_to "アカウントをお持ちの方", new_session_path(resource_name), class: 'btn btn-info btn-block' %><br /> <% end -%> <%- if devise_mapping.registerable? && controller_name != 'registrations' %> <%= link_to "アカウントをお持ちでない方", new_registration_path(resource_name), class: 'btn btn-info btn-block' %><br /> <% end -%>app/views/devise/registrations/edit.html.erb<!-- ここから --> <p><%= t('.unhappy') %> ? <%= link_to t('.cancel_my_account'), registration_path(resource_name), data: {confirm: t('.are_you_sure')}, method: :delete %> .</p> <%= link_to t('.back'), :back %> <!-- ここまでを次に置き換える --> <hr class="devise-link my-5"> <div class="form-group"> <%= link_to "トップページ", root_path, class: 'btn btn-info btn-block mb-4' %> <%= link_to t('.cancel_my_account'), registration_path(resource_name), data: {confirm: t('.are_you_sure')}, method: :delete, class: 'btn btn-danger btn-block' %> </div>これで一応単純なスタイルは完成です。
もし間違っているところがあればご教授いただけると幸いです。
- 投稿日:2020-11-17T20:02:51+09:00
混乱しがちなアソシエーションのclass_nameやforeign_keyを理解する
Railsでお知らせ機能やフォロー機能を実装しているとclass_nameやforeign_keyが出てくると思います。これらを理解するのに時間がかかったので、備忘録もかねて投稿します。
今回はお知らせ機能を例に挙げる。
イメージしやすいようER図を載せる。(最低限のカラムしか書いてません。)お知らせ機能を実装していると@user.passive_notificationsのようにして自分に来ている通知のレコードをまとめて取得したいことがある。そういう場合、下のように書く。
user.rbhas_many :passive_notifications, class_name: "Notification", foreign_key: "visited_id"少し複雑なので、簡単な例を挙げる。
Userモデルで下記のように定義した場合
よく使うhas_many :tweetsは実は色々省略されている。
user.rbhas_many :tweets # 同じ意味 has_many :tweets, class_name: "Tweets", foreign_key: "user_id"Railsで開発をするとhas_manyやbelongs_toを使ってモデルでリレーションを組むが、これを書くことによって、Userクラスへのticketsメソッドの定義が裏で行われている。例でいうと、ticketsメソッドの対象となるClassはTweetクラスで、ticketsテーブルのuser_idの値を参照してレコードを取得する。
Railsの規則によりモデルの複数形でメソッドを定義する場合、クラス名、外部キーを省略することができる。なぜならメソッド名により暗黙的にクラス名、外部キーが決定されるからである。
上記のようにメソッドを定義する(リレーションを組む)ことで、コントローラーなどで下のメソッドが使えるようになる。
user.rb@user = User.find(params[:id]) @user.tweets少し脱線したが、このことを頭に入れてお知らせ機能のリレーションをもう一度見てみる。
user.rbhas_many :passive_notifications, class_name: "Notification", foreign_key: "visited_id"ここの記述では、Userモデルにpassive_notificationsメソッドを定義している。
メソッド名からはクラス名が不明なため、class_name: "Notification"として対象のクラスを明示している。また、外部キーを設定しない場合、notificationsテーブルからuser_idのカラムを探しにいってしまうため、foreign_key: "visited_id"として定義する。これにより、@userのidとnotificationsテーブルのvisited_idとが一致したレコード、つまり@user宛の通知のレコードがまとめて取得できるようになる。
user.rb@user = User.find(params[:id]) @user.passive_notifications #ユーザ宛の通知のレコードを全件取得以上です。
まちがっていたらご指摘お願いします。
- 投稿日:2020-11-17T19:29:25+09:00
Railsのユーザ入力情報の保護機能
本投稿の目的
・Rails学習の議事録です。
学習に使った教材
Udemyの "はじめてのRuby on Rails入門-RubyとRailsを基礎から学びWebアプリケーションをネットに公開しよう" を教材として使用しました。
①Validates(バリデート)
・modelファイルに対して実施
・ユーザーが入力した値をdb保存する際の条件を指定できる
・Ex)空入力禁止,最大文字数10文字まで...qiita.rbdef model名 validates :column名, 条件: 値の指定 end【解説】
○validates :column名
⇒条件を課したいcolumn名を記載○条件: 値の指定
⇒実施したい制約に応じた記述法を記述
⇒以下に2つ例を記述する・空入力禁止
qiita.rbpresence: true・最大文字数10文字まで
qiita.rblength: { maximum: 10 }②params
・配列を入れるための箱(ハッシュ)
・ユーザーが送信したデータを一時的にparamsに格納
・その中から,必要なハッシュにヒットする値を取り出す【例: ユーザーが名前と年齢をフォームから送信した場合】
・フォームからparams へ次のように格納されるqiita.rbparams = [:name,'naoto', :age,24]・名前を取得したい場合
qiita.rbparams[:name]・年齢を取得したい場合
qiita.rbparams[:age]③strong_parameters
・指定したcolumn情報以外のフォーム受信値を無視するフィルター
【いつ役に立つ?】
・ECサイトを作成したと想定
・ユーザーがフォームでソースを操作
・ポイントcolumnを修正し残高を高めに変更
・こういった改ざん処理を止めるための設定【使いかた】
・controller中に記述(フォーム送信後のインスタンス生成時)
・引数にこのメソッドを()で指定する
・フィルタされた値がdbへ格納【例:question_paramsメソッド】
qiita.rbparams.require(:question).permit(:name, :title, :content)【paramsには以下が格納された場合を想定】
qiita.rbparams = [ question={name: 'naoto',age: 24, content: '質問内容'}, answer={name: 'kanopyo',age: 27, content: '回答内容'} ]【解説】
〇.requireについて
⇒モデル名のキーを指定〇.permitについて
⇒.requierで指定したモデルのプロパティのキーを指定
- 投稿日:2020-11-17T18:25:59+09:00
logを確認しながらrakeタスクを実装して、対象appを指定して起動しよう(Heroku)
こんにちは、株式会社ベストティーチャーでサーバーサイドエンジニアとして働き出したばかりのタワラです。
業務ではじめて学んだTipsを紹介したいと思います。
今回はrakeタスクを作成してHerokuで動作確認をする手順です。Rakeタスクを実装する手順
Tips1. ローカルでRakeタスクを作成する際には、Rails.loggerを活用する
こんな感じでコードを書いていたのですが、、、キャッシュを問い合わせるだけだから、これではログに何の情報も出ないな、動作確認はどうしよう、、、と思っているところ、、、
namespace :hogehoge do desc "適当な説明をいれる" task :foobar => :environment do Rails.cache.fetch('hoge') end end先輩「Rails.loggerを使うんだ!」
ボク「なるほど!」namespace :hogehoge do desc "適当な説明をいれる" task :foobar => :environment do Rails.logger.info('foobar started!') Rails.cache.fetch('hoge') Rails.logger.info('foobar finished!') end endこのように記述しておけば、例えばログに結果が出ない場合の処理でも、rakeタスクの作業確認ができます。こんなふうに↓
log/development.log略 foobar started! foobar finished! 略きちんと動作していることが一目瞭然なのですごい便利!
Tips2. Heroku上で確認するときはアプリ名を指定する
続いて、Heroku上で動作確認をする必要があります、↓のコマンドでrakeタスクを走らせることはできますが、、、
heroku run rake hogehoge:foobar先輩「ちょっとお待ち!」
先輩「オプションでプロジェクト名を指定するのを忘れないで!」heroku run rake hogehoge:foobar -a omosiro-project先輩「きちんとrakeタスクを起動する対象のプロジェクトなのかを確認しよう!」
先輩「こういうことでバグが起きる可能性はゼロじゃないからね」
ボク「なるほど!」終わりに
今回のTipsはこの2つ!
Tips1. ローカルでRakeタスクを作成する際には、Rails.loggerを活用する
Tips2. Heroku上で確認するときはアプリ名を指定する業務ではやはり学ぶことが多いです。
本記事が業務駆け出しの方などの役に立てば幸いです。
- 投稿日:2020-11-17T16:30:05+09:00
Railsを使ったToDoリストの作成(3.Railsの基本概念)
概要
本記事は、初学者がRailsを使ってToDoリストを作成する過程を記したものです。
私と同じく初学者の方で、Railsのアウトプット段階でつまづいている方に向けて基礎の基礎を押さえた解説をしております。
抜け漏れや説明不足など多々あるとは思いますが、読んでくださった方にとって少しでも役に立つ記事であれば幸いです。環境
Homebrew: 2.5.10
-> MacOSのパッケージ管理ツールruby: 2.6.5p114
-> RubyRails: 6.0.3.4
-> Railsnode: 14.3.0
-> Node.jsyarn: 1.22.10
-> JSのパッケージ管理ツールBundler: 2.1.4
-> gemのバージョン管理ツールiTerm$ brew -v => Homebrew 2.5.10 $ ruby -v => ruby 2.6.5p114 $ rails -v => Rails 6.0.3.4 $ npm version => node: '14.3.0' $ yarn -v => 1.22.10 $ Bundler -v => Bundler version 2.1.4第3章 Railsの基本概念
第3章では、Railsで本格的にアプリ開発をする前にRailsにおいて重要な概念を説明していきます。
1 Railsを使ってWebサイトを表示する
まずは、RailsがどのようにWebサイトを表示しているのか説明します。
Webサイトは、クライアント(ブラウザ)がリクエストを送り、サーバ(ここではRails)がレスポンス(HTMLやCSSファイルをブラウザに送る)したものを、ブラウザが解釈することで表示されています。ではRailsでは具体的にどのようなことが行われているか見ていきます。
①まず、ブラウザからGETリクエストが飛んできたら
routes.rb
で処理をします。
routes.rb
はURLを作る場所です。config/routes.rbRails.application.routes.draw do root to: 'boards#index' end?♂️root(localhost:3000)を表示してくださいというリクエストが飛んできたらBoardsControlerのindexメソッドを実行してください
②次に、コントローラです。
コントローラはroutes.rb
からブラウザからのリクエストを受け取り、モデルやビューなどと連携し結果をブラウザに返す役割を担っています。
routes.rb
の「BoardsControlerのindexメソッドを実行してください」という情報をもとに、コントローラが処理を行います。app/controllers/boards_controller.rbclass BoardsController < ApplicationController def index render 'boards/index' #この部分は省略可能 end end?♂️viewsのboardsのindex.html.hamlを表示してください
ちなみにコントローラには命名規則があります。
名前 例 コントローラ名 boards コントローラのクラス名 BoardsController コントローラのファイル名 boards_controller.rb 上記のように、コントローラ名を「boards」とした場合、コントローラのクラス名は「BoardsController」のようにコントローラ名の先頭を大文字にしてControllerを付けます。このクラスが記載されているファイル名は「コントローラ名_controller.rb」になります。
③最後に、viewsです、viewsにはブラウザに表示したいことを記述します。
app/views/boards/index.html.haml%h1 Webサイトの表示
上の画像のように表示されていれば完了です。
以上が、RailsがWebサイトを表示する仕組みです。2 MVC
1 MVCとは
MVCとは、Webフレームワークで一般的に取り入れられているアプリケーションの設定を整理するための概念の一つです。
「Model」「Views」「Controller」の頭文字をとって名付けられています。
それぞれに役割があり、ControllerがModelからデータを取得してViewsに表示するという処理が行われています。
(画像の引用元:https://snome.jp/framework/mvc-model/)2 モデルの作成
実際に、操作の対象となる「モデル」を作成します。
モデルを作成するためにはターミナルにて$rails g model [モデル名(単数形)]
のコマンドを実行します。iTerm$ rails g model Board =>create db/migrate/20201117041911_create_boards.rb create app/models/board.rb?♂️Boardモデルを作成してください
?Boardに対応するデータベースのテーブル(migrationファイル)を作成しました
?Boardに対応するRubyのクラスを作成しましたモデルの作成が完了していたら以下のようなファイルが作成されているはずです。
コメントアウトされている部分は現在のデータベースの構造(schema)のメモであり、第1章で'annotate'をインストールしたため表示されている。app/models/board.rb# == Schema Information # # Table name: boards # id :bigint not null, primary key # created_at :datetime not null # updated_at :datetime not null # class Board < ApplicationRecord endモデルが作成できたら、migrationファイルにcolumnを追加していきます。migrationファイルはデータベースのテーブルを作成するファイルです。
今回はボード(タスクのまとまり)の名前と説明をデータとして扱いたいので、'name'と'description'という2つのcolumnを追加します。
columnを追加する際はt.[データ型] :[column名]
と入力します。20201115023512_create_boards.rbclass CreateBoards < ActiveRecord::Migration[6.0] def change create_table :boards do |t| t.string :name, null: false t.text :description, null: false t.timestamps end end end※
null: false
オプション→このcolumnには絶対に値が入っていないといけないという指定ができます。今回はタスク名とタスクの説明は必須項目のため指定しています。マイグレーションをデータベース(PostgreSQL)に適用するには、ターミナルにて
$rails db:migrate
を忘れずに実行しましょう。3 ActiveRecord
ActoveRecordとは、Railsで採用されているORマッパーです。
ORマッパーとは、オブジェクトとデータベース間の関係を定義するだけで、データベースへのアクセスが行えるシステムのことです。
つまりRubyを使うことでSQLを書かなくてもデータベースにアクセスすることができるということです。
ActiveRecordはRailsにデフォルトでインストールされており、モデルの中で使うことができます。
では、なぜモデルの中で使うことができるのでしょうか?
以下2つのファイルを見てみましょう。app/models/board.rbclass Board < ApplicationRecord endapp/models/application_record.rbclass ApplicationRecord < ActiveRecord::Base self.abstract_class = true endBoardクラスはApplicationRecordクラスを継承しています。
ApplicationRecordクラスはActiveRecord::Baseクラスを継承しています。
つまりBoardクラスがActiveRecord::Baseクラスを継承しているため、モデルの中でActiveRecordを使うことができます。ActiveRecord::Baseクラスには様々なメソッドが定義されており、それらのメソッドを実行することにより、データベースにアクセスすることができます。
では具体的にどのようなメソッドが定義されているのか?
代表的なものを見ていきましょう。◆レコードを取得する
Board.find(1) #テーブルから引数に入っているidのレコードを取得する Board.find_by({column名: ''}) #テーブルからcolumn名でレコードを取得する #Board.find_by(column名: '') -> {}を省略しても良い Board.first,second,third #テーブルからidが一番若いレコードを取得する Board.last #テーブルからidが一番大きいレコードを取得する Board.all #テーブルから全てのレコードをを配列として取得する Board.all.order(:id) #レコードををid順に並び替えて取得する #逆順に並べ替えて取得する時は(id: :desc) Board.all.limit(3) #テーブルから引数に入っている数だけidの若い順にレコードを取得する Board.where('id > ?', 2) #引数の条件に合ったレコードを全て取得する #条件は文字列で渡す必要がある Board.count #データの件数を数える◆データの作成・削除
Board.create({name: '', description: ''}) #引数の値を元にインスタンスを作成し、DBに保存する #board = Board.new({name: 'new', description: 'new'}) -> 空の箱を作る #board.save -> レコードをDBに保存する Board.save #createとは違って保存機能のみ Board.update #取得したレコードを更新し保存する #Board.last -> 対象となるデータを取ってくる #Board.last.update({column名: ''}) -> 更新する Board.assign_attributes #取得したレコードを更新するが保存はしない #board = Board.last #board.assign_attributes(title: 'assigned') #board.save Board.destroy #取得したレコードを削除する #board = Board.last -> 削除する対象のデータを取得する #board.destroy`では実際に、ターミナルでコンソールを立ち上げてデータを作成してみましょう。
ターミナルでコンソールを立ち上げるためには以下のコマンドを実行します。iTerm$ rails cコンソールが立ち上がったら、createメソッドを実行しましょう。
iTermirb(main):001:0> Board.create(name: 'console-name1', description: 'console-description1') (0.2ms) BEGIN Board Create (4.4ms) INSERT INTO "boards" ("name", "description", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["name", "console-name1"], ["description", "console-description1"], ["created_at", "2020-11-17 05:38:06.272799"], ["updated_at", "2020-11-17 05:38:06.272799"]] (1.5ms) COMMIT => #<Board id: 1, name: "console-name1", description: "console-description1", created_at: "2020-11-17 05:38:06", updated_at: "2020-11-17 05:38:06">createメソッドを実行することにより、SQLが実行され、テーブルにデータが保存されます。
では、実際に保存されているのかActiveRecord::Baseクラスのallメソッドを使って確かめてみましょう。iTermirb(main):002:0> Board.all Board Load (0.9ms) SELECT "boards".* FROM "boards" LIMIT $1 [["LIMIT", 11]] => #<ActiveRecord::Relation [#<Board id: 1, name: "console-name1", description: "console-description1", created_at: "2020-11-17 05:38:06", updated_at: "2020-11-17 05:38:06">]>下記のようにデータが作成されていることがわかると思います。
Board id: 1,
name: "console-name1",
description: "console-description1",
created_at: "2020-11-17 05:38:06",
updated_at: "2020-11-17 05:38:06">以上でActiveRecordの説明は終わりです。
コンソールを終了したい時はexit
と入力しましょう。
- 投稿日:2020-11-17T14:39:33+09:00
Rails覚書
- 投稿日:2020-11-17T14:21:10+09:00
AWS メモ
ElastiCacheにおけるRadis/Memcache
EastiCacheにおけるradis
・複雑なデータ型が必要である。
・インメモリデータセットをソートまたはランク付けする必要がある。
・読込処理の負荷に対して、リードレプリカにレプリケートする必要がある。
・pub/sub機能が必要
・自動的なフェイルオーバーが必要である
・キーストアの永続性が必要である。
・バックアップと復元の機能が必要である。・複数のデータベースをサポートする必要がある
*提供/シングルスレッドで動作 インメモリDB
すべてのデータ操作は排他的ElastiCacheにおけるMemcached
・シンプルなデータ型が必要である
・複数のコアまたはスレッドを持つ大きなノードを実行する必要がある。
・システムでの需要の増減に応じてノードを追加または削除するスケールアウトおよびスケールイン機能が必要である。
・データベースなどのオブジェクトをキャッシュする必要がある。
・キーストアの永続性は必要ない
・バックアップと復元の機能が必要でない
・複数のデータベースを利用できない
- 投稿日:2020-11-17T14:06:46+09:00
【Rails6.0】ポートフォリオに必須な「かんたんログイン」実装手順
概要
Railsでアプリを作成する際、deviseを用いてログイン機能を実装する方は多いかと思います。
中でも、ポートフォリオには「かんたんログインをつけよう!」
と耳にした方も多いかと。しかし、私の経験談として、
実装には結構苦労しました
。
理由としては、シンプルに「devise関連で、知らなかったことをやる必要があったから」
です。なので今回の記事では、
- deviseのコントローラをいじらなきゃいけないの?
- ルーティングはどうやって設定する?
このあたりを整理しつつ、「かんたんログイン実装手順」をアウトプットしたいと思います。
なお、この記事はこちらの素晴らしい記事を参考にしています。
YouTubeでの詳しい解説もありますので、ぜひ御覧ください。Qiita | 簡単ログイン・ゲストログイン機能の実装方法(ポートフォリオ用)
環境
- macOS Catalina 10.15.6
- ruby 2.6.5
- Rails 6.0.3.4
- MySQL : 5.6.47
- devise : 4.7.3
実装の流れを確認
実装に入る前に、まず
かんたんログインの流れ
をおさえましょう。かんたんログインの流れ1. かんたんログインボタンクリック 2-1. 任意のメアド(guest@gmail.com)がusersテーブルにない場合は他カラムを追加しユーザー作成 2-2. 任意のメアド(guest@gmail.com)がusersテーブルにある場合はそれを取得 3. 作成・取得したユーザーでログイン 4. 好きなページにリダイレクト(大体はトップページ)つまり、必要なのは次の工程です。
必要な工程1. ルーティングにかんたんログイン用コントローラのパスを設定 2. かんたんログイン用コントローラーを作成 3. リンクをビューに設置この工程に沿って実装していきます。
実装 : ファイルを3つ編集すればOK
それでは、
ルーティング→コントローラ→ビューの順番で実装していきます
。
1. かんたんログイン用ルーティングの設定
ルーティングファイルに以下の記述を追加して下さい。
routes.rbdevise_scope :user do post 'users/guest_sign_in', to: 'users/guest_sessions#new_guest' endこれにより、次のルーティングが生成します。
Prefix Verb URI Pattern Controller#Action users_guest_sign_in POST /users/guest_sign_in users/guest_sessions#new_guest この内容を整理します。
- post, users_guest_sign_in_pathで
app/controllers/users/guest_sessions_controller.rb
のnew_guestアクション
を指定というわけで、指定したディレクトリにコントローラを作成して、かんたんログインアクションを実装しましょう。
2. かんたんログイン用コントローラの作成
app/controllers/users/guest_sessions_controller.rb
にnew_guest
アクションを実装していきます。
- 名前空間とRailsの決まりごとに従ってクラス名を選択
- DeviseのSessionsControllerを継承
guest@gmail.com
の有無でユーザーの作成or取得を変更以上の点に注意しましょう。
app/controllers/users/guest_sessions_controller.rbclass Users::SessionsController < Devise::SessionsController def new_guest user = User.find_or_create_by!(email: "guest@gmail.com") do |user| # ブロックで必要カラムを追加(自分の場合はnicknameを追加) user.nickname = "テストユーザー" user.password = SecureRandom.urlsafe_base64 # user.confirmed_at = Time.now # Confirmable を使用している場合は必要 end # ログイン(deviseのメソッド) sign_in user # トップページへリダイレクト redirect_to root_path end end
find_or_create_by!
は次のようなメソッドです。
- find_by(カラム: 値)で該当するレコードを取得
- 見つからない場合は、createで新規レコードを作成
- ! は例外を発生させる記述 (! がない場合は、ログインされないままただリダイレクトされる)
ちなみに、今回は実装するコードについての示すのが目的なので、名前空間とパスワードの部分については以下の記事を見て補完して下さい (なお、Qiitaは僕が書いた記事です)。
- Rubyリファレンスマニュアル | SecureRandomモジュール
- Qiita | 【Rails/ルーティング】自作したディレクトリ内のコントローラを参照する方法
- Qiita | 【Ruby/namespace】Rubyでよく見る「:: ←これ」を深ぼる
3. ビューにリンクを設置
かんたんログインボタンを実装したいビューにリンクを設置しましょう。
僕のアプリの場合は、トップページに以下のコードを追加しました。トップページ<%= link_to 'ゲストログイン', users_guest_sign_in_path, method: :post %>これで完了です!
(以下のGIFでは自分のアプリ用に詳細を変更しています)
4. (補足) 一部をUserモデルに移植する
DBとやりとりしてレコードを取得・生成するのはモデルの仕事です。
なので次のように切り離すとベターかと思います。app/controllers/users/guest_sessions_controller.rbclass Users::SessionsController < Devise::SessionsController def new_guest user = User.guest # ログイン(deviseのメソッド) sign_in user # トップページへリダイレクト redirect_to root_path end endapp/models/user.rbclass User < ApplicationRecord # (略) def self.guest find_or_create_by!(email: "guest@gmail.com") do |user| user.nickname = "テストユーザー" user.password = SecureRandom.urlsafe_base64 end end end
まとめ
- かんたんログイン実装には、以下のファイルを作成or編集すればOK
- かんたんログインアクションへのルーティング
- deviseコントローラを継承した「かんたんログイン用コントローラ」
- ボタンを設置したいビュー
いろんな記事があると思いますが、基本はこちらに示したとおりなので、ぜひ参考にして下さい!
- 投稿日:2020-11-17T11:58:09+09:00
[Rails]カテゴリー機能
はじめに
アプリ開発において、ancestryというgemを用いてカテゴリー機能を加えたのでまとめました。
目次
- カテゴリー選択・保存
- カテゴリー検索表示
- カテゴリー検索結果表示
1. カテゴリー選択・保存
categoriesテーブルの作成
ancestryをインストールします。
gemfilegem 'ancestry'次にcategoryモデルを作成します。
ターミナル
rails g model categoryhas_ancestryを記述します。
app/models/category.rbclass Category < ApplicationRecord has_many :posts has_ancestry end以下のようにマイグレーションファイルに記述します。
indexについてはこちらdb/migrate/20XXXXXXXXXXXX_create_categories.rbclass CreateCategories < ActiveRecord::Migration[6.0] def change create_table :categories do |t| t.string :name, index: true, null: false t.string :ancestry, index: true t.timestamps end end endgoogleスプレッドシートにカテゴリーを記述していきます。
Aの列がid、Bの列がname(カテゴリー名)、Cの列がancestry(親子孫を見分ける数値)となります。
データの保存方法は、ファイル → ダウンロード → カンマ区切りの値(.csv 現在のシート) の手順で保存できます。ダウンロードしたcsvファイルはdbフォルダに配置します。
seeds.rbファイル内へ以下の通り記述します。
db/seeds.rbrequire "csv" CSV.foreach('db/category.csv') do |row| Category.create(:id => row[0], :name => row[1], :ancestry => row[2]) endターミナルでrails db:seedコマンドを実行するとcsvファイルを読み込み自動でDBのレコードが生成されます。
foreachの後に読み込みたいファイルの指定を行います。
その下の記述については、モデル名.create(カラム名 => 読み込みたい列)となります。
row[0] → Aの列がid
row[1] → Bの列がname(カテゴリー名)
row[2] → Cの列がancestry(親子孫を見分ける数値)ルーティング
子、孫カテゴリーをjson形式でルーティングを設定します。
config/routes.rbRails.application.routes.draw do ~略~ resources :posts do collection do get 'top' get 'get_category_children', defaults: { format: 'json' } get 'get_category_grandchildren', defaults: { format: 'json' } get 'name_search' end ~略~ endコントローラー
postsコントローラーに親カテゴリーを定義します。
複数箇所で使用するためbefore_actionを使って定義します。app/controllers/posts_controller.rbdef set_parents @parents = Category.where(ancestry: nil) endpostsコントローラーに子、孫カテゴリーのメソッドを定義します。
app/controllers/posts_controller.rbdef get_category_children @category_children = Category.find("#{params[:parent_id]}").children end def get_category_grandchildren @category_grandchildren = Category.find("#{params[:child_id]}").children endjson.jbuilderファイルを作成し、jsonデータへ変換します。
app/views/posts/get_category_children.json.jbuilderjson.array! @category_children do |child| json.id child.id json.name child.name endapp/views/posts/get_category_grandchildren.json.jbuilderjson.array! @category_grandchildren do |grandchild| json.id grandchild.id json.name grandchild.name endビュー
javascriptでカテゴリー選択時の動作を設定します。
:app/javascript/category_post.js $(function(){ function appendOption(category){ var html = `<option value="${category.id}">${category.name}</option>`; return html; } function appendChildrenBox(insertHTML){ var childSelectHtml = ""; childSelectHtml = `<div class="category__child" id="children_wrapper"> <select id="child__category" name="post[category_id]" class="serect_field"> <option value="">---</option> ${insertHTML} </select> </div>`; $('.append__category').append(childSelectHtml); } function appendGrandchildrenBox(insertHTML){ var grandchildSelectHtml = ""; grandchildSelectHtml = `<div class="category__child" id="grandchildren_wrapper"> <select id="grandchild__category" name="post[category_id]" class="serect_field"> <option value="">---</option> ${insertHTML} </select> </div>`; $('.append__category').append(grandchildSelectHtml); } $('#item_category_id').on('change',function(){ var parentId = document.getElementById('item_category_id').value; if (parentId != ""){ $.ajax({ url: '/posts/get_category_children/', type: 'GET', data: { parent_id: parentId }, dataType: 'json' }) .done(function(children){ $('#children_wrapper').remove(); $('#grandchildren_wrapper').remove(); var insertHTML = ''; children.forEach(function(child){ insertHTML += appendOption(child); }); appendChildrenBox(insertHTML); if (insertHTML == "") { $('#children_wrapper').remove(); } }) .fail(function(){ alert('カテゴリー取得に失敗しました'); }) }else{ $('#children_wrapper').remove(); $('#grandchildren_wrapper').remove(); } }); $('.append__category').on('change','#child__category',function(){ var childId = document.getElementById('child__category').value; if(childId != ""){ $.ajax({ url: '/posts/get_category_grandchildren', type: 'GET', data: { child_id: childId }, dataType: 'json' }) .done(function(grandchildren){ $('#grandchildren_wrapper').remove(); var insertHTML = ''; grandchildren.forEach(function(grandchild){ insertHTML += appendOption(grandchild); }); appendGrandchildrenBox(insertHTML); if (insertHTML == "") { $('#grandchildren_wrapper').remove(); } }) .fail(function(){ alert('カテゴリー取得に失敗しました'); }) }else{ $('#grandchildren_wrapper').remove(); } }) });新規投稿ページにカテゴリーセレクトボックスを表示させます。
app/views/posts/new.html.erb<div class="append__category"> <div class="category"> <div class="form__label"> <div class="weight-bold-text lavel__name "> カテゴリー </div> <div class="lavel__Required"> <%= f.collection_select :category_id, @parents, :id, :name,{ include_blank: "選択してください"},class:"serect_field", id:"item_category_id" %> </div> </div> </div> </div>2. カテゴリー検索表示
コントローラー
app/controllers/posts_controller.rbdef top respond_to do |format| format.html format.json do if params[:parent_id] @childrens = Category.find(params[:parent_id]).children elsif params[:children_id] @grandChilds = Category.find(params[:children_id]).children elsif params[:gcchildren_id] @parents = Category.where(id: params[:gcchildren_id]) end end end endビュー
javascriptでどの親カテゴリーにの上にマウスがいるのか、それに属する子カテゴリーや孫カテゴリーを取得しています。
:app/javascript/category.js $(document).ready(function () { // 親カテゴリーを表示 $('#categoBtn').hover(function (e) { e.preventDefault(); e.stopPropagation(); $('#tree_menu').show(); $('.categoryTree').show(); }, function () { // あえて何も記述しない }); // 非同期にてヘッダーのカテゴリーを表示 function childBuild(children) { let child_category = ` <li class="category_child"> <a href="/posts/${children.id}/search"><input class="child_btn" type="button" value="${children.name}" name= "${children.id}"> </a> </li> ` return child_category; } function gcBuild(children) { let gc_category = ` <li class="category_grandchild"> <a href="/posts/${children.id}/search"><input class="gc_btn" type="button" value="${children.name}" name= "${children.id}"> </a> </li> ` return gc_category; } // 親カテゴリーを表示 $('#categoBtn').hover(function (e) { e.preventDefault(); e.stopPropagation(); timeOut = setTimeout(function () { $('#tree_menu').show(); $('.categoryTree').show(); }, 500) }, function () { clearTimeout(timeOut) }); // 子カテゴリーを表示 $('.parent_btn').hover(function () { $('.parent_btn').css('color', ''); $('.parent_btn').css('background-color', ''); let categoryParent = $(this).attr('name'); timeParent = setTimeout(function () { $.ajax({ url: '/posts/top', type: 'GET', data: { parent_id: categoryParent }, dataType: 'json' }) .done(function (data) { $(".categoryTree-grandchild").hide(); $(".category_child").remove(); $(".category_grandchild").remove(); $('.categoryTree-child').show(); data.forEach(function (child) { let child_html = childBuild(child) $(".categoryTree-child").append(child_html); }); $('#tree_menu').css('max-height', '490px'); }) .fail(function () { alert("カテゴリーを選択してください"); }); }, 400) }, function () { clearTimeout(timeParent); }); // 孫カテゴリーを表示 $(document).on({ mouseenter: function () { $('.child_btn').css('color', ''); $('.child_btn').css('background-color', ''); let categoryChild = $(this).attr('name'); timeChild = setTimeout(function () { $.ajax({ url: '/posts/top', type: 'GET', data: { children_id: categoryChild }, dataType: 'json' }) .done(function (gc_data) { $(".category_grandchild").remove(); $('.categoryTree-grandchild').show(); gc_data.forEach(function (gc) { let gc_html = gcBuild(gc) $(".categoryTree-grandchild").append(gc_html); let parcol = $('.categoryTree').find(`input[name="${gc.root}"]`); $(parcol).css('color', 'white'); $(parcol).css('background-color', '#b1e9eb'); }); $('#tree_menu').css('max-height', '490px'); }) .fail(function () { alert("カテゴリーを選択してください"); }); }, 400) }, mouseleave: function () { clearTimeout(timeChild); } }, '.child_btn'); // 孫カテゴリーを選択時 $(document).on({ mouseenter: function () { let categoryGc = $(this).attr('name'); timeGc = setTimeout(function () { $.ajax({ url: '/posts/top', type: 'GET', data: { gcchildren_id: categoryGc }, dataType: 'json' }) .done(function (gc_result) { let childcol = $('.categoryTree-child').find(`input[name="${gc_result[0].parent}"]`); $(childcol).css('color', 'white'); $(childcol).css('background-color', '#b1e9eb'); $('#tree_menu').css('max-height', '490px'); }) .fail(function () { alert("カテゴリーを選択してください"); }); }, 400) }, mouseleave: function () { clearTimeout(timeGc); } }, '.gc_btn'); // カテゴリー一覧ページのボタン $('#all_btn').hover(function (e) { e.preventDefault(); e.stopPropagation(); $(".categoryTree-grandchild").hide(); $(".categoryTree-child").hide(); $(".category_grandchild").remove(); $(".category_child").remove(); }, function () { // あえて何も記述しないことで親要素に外れた際のアクションだけを伝搬する }); // カテゴリーを非表示(カテゴリーメニュから0.8秒以上カーソルを外したら消える) $(document).on({ mouseleave: function (e) { e.stopPropagation(); e.preventDefault(); timeChosed = setTimeout(function () { $(".categoryTree-grandchild").hide(); $(".categoryTree-child").hide(); $(".categoryTree").hide(); $(this).hide(); $('.parent_btn').css('color', ''); $('.parent_btn').css('background-color', ''); $(".category_child").remove(); $(".category_grandchild").remove(); }, 800); }, mouseenter: function () { timeChosed = setTimeout(function () { $(".categoryTree-grandchild").hide(); $(".categoryTree-child").hide(); $(".categoryTree").hide(); $(this).hide(); $('.parent_btn').css('color', ''); $('.parent_btn').css('background-color', ''); $(".category_child").remove(); $(".category_grandchild").remove(); }, 800); clearTimeout(timeChosed); } }, '#tree_menu'); // カテゴリーボタンの処理 $(document).on({ mouseenter: function (e) { e.stopPropagation(); e.preventDefault(); timeOpened = setTimeout(function () { $('#tree_menu').show(); $('.categoryTree').show(); }, 500); }, mouseleave: function (e) { e.stopPropagation(); e.preventDefault(); clearTimeout(timeOpened); $(".categoryTree-grandchild").hide(); $(".categoryTree-child").hide(); $(".categoryTree").hide(); $("#tree_menu").hide(); $(".category_child").remove(); $(".category_grandchild").remove(); } }, '.header__headerInner__nav__listsLeft__item'); });トップ画面にカテゴリー選択ウィンドウをセットします。
app/views/posts/top.html.erb<div class="item-categories"> <h2> カテゴリー一覧 </h2> <%= link_to posts_path, class: "category-button", id: 'categoBtn' do %> カテゴリーから探す <% end %> <div id="tree_menu"> <ul class="categoryTree"> <% @parents.each do |parent| %> <li class="category_parent"> <%= link_to search_post_path(parent) do %> <input type="button" value="<%= parent.name %>" name="<%= parent.id %>" class="parent_btn"> <% end %> </li> <% end %> </ul> <ul class="categoryTree-child"> </ul> <ul class="categoryTree-grandchild"> </ul> </div> </div>3. カテゴリー検索結果表示
ルーティング
カテゴリーをidで区別するため、memberを用いてsearchアクションを定義しています。
config/routes.rbresources :posts do ~略~ member do get 'search' end ~略~ endコントローラー
クリックしたカテゴリーが、親カテゴリー、子カテゴリー、孫カテゴリーのどれなのかで条件分岐しています。
app/controllers/posts_controller.rbdef search @category = Category.find_by(id: params[:id]) if @category.ancestry == nil category = Category.find_by(id: params[:id]).indirect_ids if category.empty? @posts = Post.where(category_id: @category.id).order(created_at: :desc) else @posts = [] find_item(category) end elsif @category.ancestry.include?("/") @posts = Post.where(category_id: params[:id]).order(created_at: :desc) else category = Category.find_by(id: params[:id]).child_ids @posts = [] find_item(category) end end def find_item(category) category.each do |id| post_array = Post.where(category_id: id).order(created_at: :desc) if post_array.present? post_array.each do |post| if post.present? @posts.push(post) end end end end endビュー
app/views/posts/search.html.erb<div class="item-categories"> <h2> カテゴリー一覧 </h2> <%= link_to posts_path, class: "category-button", id: 'categoBtn' do %> カテゴリーから探す <% end %> <div id="tree_menu"> <ul class="categoryTree"> <% @parents.each do |parent| %> <li class="category_parent"> <%= link_to search_post_path(parent) do %> <input type="button" value="<%= parent.name %>" name="<%= parent.id %>" class="parent_btn"> <% end %> </li> <% end %> </ul> <ul class="categoryTree-child"> </ul> <ul class="categoryTree-grandchild"> </ul> </div> </div>参考リンク
https://qiita.com/k_suke_ja/items/aee192b5174402b6e8ca
https://qiita.com/Sobue-Yuki/items/9c1b05a66ce6020ff8c1 https://qiita.com/dr_tensyo/items/88e8ddf0f5ce37040dc8
https://qiita.com/ATORA1992/items/bd824f5097caeee09678
https://qiita.com/misioro_missie/items/175af1f1678e76e59dea
https://qiita.com/Rubyist_SOTA/items/49383aa7f60c42141871
- 投稿日:2020-11-17T11:46:27+09:00
HTML.CSS.JavaScript.Ruby.Rails概要
HTML
HTML(Hyper Text Markup Language)とはWEBページを作成する際に使用されるマークアップ言語である。
WEBページのほとんどにHTMLが使用されている。
HTMLはタグを使いコンピューターに命令を出す事により見出しを付けたり段落を付けたりと、WEBページのレイアウト、構成を形作ることができる。CSS
CSS(Cascading Style Sheets)は先ほどのHTMLと組み合わせて使用する言語である。
CSSは文字の色やサイズ、レイアウトを変えたり、WEBページを装飾する言語である。
HTMLでもWEBページの装飾をすることは出来るが、CSSの役割なので分けて使う必要がある。JavaScript
WEBサイトに動きをつけるためのプログラミング言語である。
具体的には文章や画像を拡大表示したり、より動的なWEBサイトを作ることができる。
サーバーを介さずにブラウザ上で動かすことができる。またこのようなプログラムをクライアントサイド・スクリプトというRuby
Rubyとは日本人であるまつもとゆきひろ氏によって作成されたオブジェクト指向スクリプト言語である。
WEBサイトやECサイトなどの製作、SNS開発など様々なことができる。有名なサイトではぐるなびや食べログなど
Rubyは他の言語に比べシンプルなコードのため開発スピードが早く読みやすいRails
Ruby on RailsとはRubyを使用したフレームワークである。
フレームワークとは雛形のことで、一からプログラミングをしなくても枠組みが用意されているため開発時間を大幅に削減することができる。他にもSinatraやHANAMIなどがある
- 投稿日:2020-11-17T11:30:30+09:00
モデルとテーブル作成
モデルとテーブルの作成を備忘録として残す。
ターミナルでモデルを作成
rails g model モデル名下記のログが出ればOK
invoke active_record create db/migrate/000000000_create_tweets.rb create app/models/モデル名.rb invoke test_unit create test/models/モデル名_test.rb create test/fixtures/モデル名.ymlモデル名.rbが作成される。
テーブルの作成
モデルを生成すると一緒にマイグレーションファイルが作られる。
dbフォルダのmigrateファイルにある。これを編集する。class Createモデル名(頭文字は大文字) < ActiveRecord::Migration[6.0] def change create_table :モデル名 do |t| t.string :name t.string :text t.text :image t.timestamps end end endt.あとが「型」、:に続くのが「カラム名」です。
型の種類
string : 文字列
text : 長い文字列
integer : 整数
float : 浮動小数
decimal : 精度の高い小数
datetime : 日時
timestamp : タイムスタンプ
time : 時間
date : 日付
binary : バイナリデータ
boolean : Booleanマイグレーションの実行
rails db:migrate以下のようなログが出ればOK
== 20xxxxxxxxxx CreateTweets: migrating ===================================== -- create_table(:tweets) -> 0.0148s == 20xxxxxxxxxx CreateTweets: migrated (0.0149s) ============================最後に、rails s でローカルサーバを再起動
rails c でデータが保存されていることを確認[1] pry(main)> Tweet.create(name: "take", text: "apple")以下のようにログが出てデータの保存ができているのを確認
(4.4ms) SET NAMES utf8, @@SESSION.sql_mode = CONCAT(CONCAT(@@sql_mode, ',STRICT_ALL_TABLES'), ',NO_AUTO_VALUE_ON_ZERO'), @@SESSION.sql_auto_is_null = 0, @@SESSION.wait_timeout = 2147483 (0.3ms) BEGIN Tweet Create (0.7ms) INSERT INTO `tweets` (`name`, `text`, `created_at`, `updated_at`) VALUES ('take', 'apple', '2020-11-17 03:06:52.758040', '2020-11-17 03:06:52.758040') (3.2ms) COMMIT => #<Tweet:0x00007ff6e6ca6770 id: 1, name: "take", text: "apple", image: nil, created_at: Tue, 17 Nov 2020 03:06:52 UTC +00:00, updated_at: Tue, 17 Nov 2020 03:06:52 UTC +00:00>確認できたら完了!!
- 投稿日:2020-11-17T11:24:25+09:00
Rails5.1+puma ローカルのproduction環境でSSL接続する
Rails5.0から5.1にアップグレードし、ローカルでproductionの動作確認しようとしたら、何かとつまずいたので諸問題の解決方法をまとめておきます。
まず、
rails s -e production
を叩いたところ、この接続にはセキュリティの問題があるのでWEBページを表示できません的なエラーが出ました。httpsにしないとダメなようです。SSLで接続する
基本的なやり方は、こちらの記事を参照させていただきました。
Rails5 + pumaのローカル環境でSSL/HTTPSを有効にする上記記事は、opensslで証明書発行してましたが、エラーが出たので証明書の発行をmkcertでやりました。
mkcertでの証明書の発行のやり方は、こちらの記事が参考になりました。
ローカル環境でSSLをオレオレ証明書で行っていて警告が出てる人に朗報まず、上記記事に従ってmkcertで証明書を発行、appフォルダ以下の適当な場所にserver.key、server.crtファイルを置きます。
Rails5 + pumaのローカル環境でSSL/HTTPSを有効にする を参考にpuma.rbを設定。
bundle exec pumactl start -e production
でサーバーを起動。css/jsが表示されなくなった問題の解決
SSLで接続できたものの、
HTTP parse error, malformed request ()
というエラーが出て、application.js、application.cssが読み込まれませんでした。
jsとcssのサーバーがhttp://0.0.0.0:3000 になっていたので、asset_hostの設定を以下のように修正。ローカルと本番環境で変えられるよう、環境変数で定義しました。config/environments/production.rbconfig.action_controller.asset_host = "https://#{ENV['HOST_URI']}"参考
- StackOverflow::Ruby on Railsの本番環境でpublic/assetsが参照できないpublic/images以下のファイルの表示問題解決
上記の対応でcssとjsは出るようになったけれど、public/images以下に置いていた画像ファイルがまだ表示されませんでした。
Rails5.1からは、asset_pipelineでプリコンパイルしない静的なファイルは、ヘルパーで呼び出すときには以下の用に書きます。= image_tag 'hogehoge.png', skip_pipeline: trueしかし、これが設定してあってもダメだったので、別の問題のようです。
対応1
config.public_file_server.enabled
をtrueにconfig/environments/production.rb- config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? + config.public_file_server.enabled = true対応2
assets:precompileをやり直す。# 思い切って一度全部消したければ、先にこちら RAILS_ENV=production bundle exec rake assets:clobber # assets:cleanは古いバージョンのファイルを消す。clobberをやってたらcleanは不要 RAILS_ENV=production bundle exec rake assets:precompile assets:clean上記対応で、assetすべて表示されるようになりました。
参考
- 投稿日:2020-11-17T11:22:48+09:00
Railsを使ったToDoリストの作成(2.hamlの導入と使い方)
概要
本記事は、初学者がRailsを使ってToDoリストを作成する過程を記したものです。
私と同じく初学者の方で、Railsのアウトプット段階でつまづいている方に向けて基礎の基礎を押さえた解説をしております。
抜け漏れや説明不足など多々あるとは思いますが、読んでくれた方にとって少しでも役に立つ記事であれば幸いです。環境
Homebrew: 2.5.10
-> MacOSのパッケージ管理ツールruby: 2.6.5p114
-> RubyRails: 6.0.3.4
-> Railsnode: 14.3.0
-> Node.jsyarn: 1.22.10
-> JSのパッケージ管理ツールBundler: 2.1.4
-> gemのバージョン管理ツールiTerm$ brew -v => Homebrew 2.5.10 $ ruby -v => ruby 2.6.5p114 $ rails -v => Rails 6.0.3.4 $ npm version => node: '14.3.0' $ yarn -v => 1.22.10 $ Bundler -v => Bundler version 2.1.4第2章 hamlの導入
第2章では、Railsでアプリ開発をする時にhamlテンプレートを使えるように設定していきます。
Railsはデフォルトはerbテンプレートが使われていますが、erbテンプレートは使いづらい上に、現場レベルでもあまり使われていないみたいなので、今回の開発においてはhamlテンプレートを使っていきたいと思います。1 hamlの導入
まずは、hamlテンプレートをRailsで使えるようにしていきます。
hamlitというgemとerb2hamlというgemをインストールします。Gemfilegem 'hamlit' group :development do gem 'erb2haml' end?♂️
hamlit
というgemをインストールしてください
?♂️erb2haml
という変換ツールのgemをインストールしてくださいターミナルで
$bundle install
を行えばgemのインストールは完了です。既存のerbファイルをhamlに置き換えるためにはターミナルにて以下のコマンドを実行する必要があります。
iterm$ bundle exec rake haml:replace_erbs⚠︎hamlitをインストールしてエラーが起こってしまった場合は
$rails s
でサーバを立ち上げ直しましょう
以上でRailsの開発においてhamlテンプレートを使うことができるようになりました。
次は、実際にhamlの書き方を見ていきます。2 hamlの基本ルール①
① タグの書き方
erb<header> <p></p> </header>haml%header %p
- hamlでは閉じタグが必要ない
- 入れ子構造にする際はスペースキーを2回押すこと
② class/id/attributeの書き方
erb<header class="header"> <p id="id"></p> <a href="https://haml.com"></a> </header>haml%header.header %p#id %a{href: "https://haml.com"}
- classは
.class名
で指定する- idは
#id名
で指定する- attributeはハッシュで指定する
③divタグの書き方
erb<div class="container"></div>haml.container
- divタグを作成したい時はタグの表記は必要ない。hamlが勝手に察してdivタグを付けてくれる
3 hamlの基本ルール②
①Rubyのコードの埋め込み(画面に表示する場合)
erb<%= board.title %>haml= board.title
- <%=%>を表現する際は
=
を使う②Rubyのコードの埋め込み(画面に表示しない場合)
erb<% Board.all.each do |board| %> <%= board.title %> <% end %>haml- Board.all.each do |board| = board.title
- <%%>を表現する際は
-
を使う- <% end %>はhamlが勝手に付けてくれる
※もしhamlの書き方がわからなくなったら下記のサイトで確認しましょう。
- 投稿日:2020-11-17T11:22:48+09:00
Railsを使ったToDoリストの作成(2.hamlの導入と書き方)
概要
本記事は、初学者がRailsを使ってToDoリストを作成する過程を記したものです。
私と同じく初学者の方で、Railsのアウトプット段階でつまづいている方に向けて基礎の基礎を押さえた解説をしております。
抜け漏れや説明不足など多々あるとは思いますが、読んでくださった方にとって少しでも役に立つ記事であれば幸いです。環境
Homebrew: 2.5.10
-> MacOSのパッケージ管理ツールruby: 2.6.5p114
-> RubyRails: 6.0.3.4
-> Railsnode: 14.3.0
-> Node.jsyarn: 1.22.10
-> JSのパッケージ管理ツールBundler: 2.1.4
-> gemのバージョン管理ツールiTerm$ brew -v => Homebrew 2.5.10 $ ruby -v => ruby 2.6.5p114 $ rails -v => Rails 6.0.3.4 $ npm version => node: '14.3.0' $ yarn -v => 1.22.10 $ Bundler -v => Bundler version 2.1.4第2章 hamlの導入と書き方
第2章では、Railsでアプリ開発をする時にhamlテンプレートを使えるように設定していきます。
Railsはデフォルトはerbテンプレートが使われていますが、erbテンプレートは使いづらい上に、現場レベルでもあまり使われていない(らしい)ので、今回の開発においてはhamlテンプレートを使っていきたいと思います。1 hamlの導入
まずは、hamlテンプレートをRailsで使えるようにしていきます。
hamlitというgemとerb2hamlという変換ツールのgemをインストールします。Gemfilegem 'hamlit' group :development do gem 'erb2haml' end?♂️
hamlit
というgemをインストールしてください
?♂️erb2haml
というgemをインストールしてくださいターミナルで
$bundle install
を行えばgemのインストールは完了です。既存のerbファイルをhamlに置き換えるためにはターミナルにて以下のコマンドを実行する必要があります。
iterm$ bundle exec rake haml:replace_erbs⚠︎hamlitをインストールしてエラーが起こってしまった場合は
$rails s
でサーバを立ち上げ直しましょう
以上でRailsの開発においてhamlテンプレートを使うことができるようになりました。
次は、実際にhamlの書き方を見ていきます。2 hamlの基本ルール①
① タグの書き方
erb<header> <p></p> </header>haml%header %p
- hamlでは閉じタグが必要ない
- 入れ子構造にする際はスペースキーを2回押すこと
② class/id/attributeの書き方
erb<header class="header"> <p id="id"></p> <a href="https://haml.com"></a> </header>haml%header.header %p#id %a{href: "https://haml.com"}
- classは
.class名
で指定する- idは
#id名
で指定する- attributeはハッシュで指定する
③divタグの書き方
erb<div class="container"></div>haml.container
- divタグを作成したい時はタグの表記は必要ない。hamlが勝手に察してdivタグを付けてくれる
3 hamlの基本ルール②
①Rubyのコードの埋め込み(画面に表示する場合)
erb<%= board.title %>haml= board.title
- <%=%>を表現する際は
=
を使う②Rubyのコードの埋め込み(画面に表示しない場合)
erb<% Board.all.each do |board| %> <%= board.title %> <% end %>haml- Board.all.each do |board| = board.title
- <%%>を表現する際は
-
を使う- <% end %>はhamlが勝手に付けてくれる
※もしhamlの書き方がわからなくなったら下記のサイトで確認しましょう。
- 投稿日:2020-11-17T11:08:57+09:00
Rails SecureRandom.alphanumericを使ってテストアカウントのパスワードを生成する
railsでアプリケーションを作成しています。
テストアカウントでのログイン機能を実装したときにエラーが発生したため、自分が行った解決方法を記録として残しておきます。開発環境
Ruby 2.6.5
Rails 6.0.3.3実装内容
以下のコードでテストログイン機能の実装を行いました。
コントローラー
class Users::SessionsController < Devise::SessionsController # ゲストユーザーとしてログイン def new_guest user = User.find_or_create_by!(nickname: 'ゲストユーザー', email: 'user@example.com') do |user| user.password = SecureRandom.alphanumeric end sign_in user redirect_to root_path, notice: 'ゲストユーザーとしてログインしました。' end end
モデル
#半角英数字のみを許可するバリデーションを設定 PASSWORD_REGEX = /\A(?=.*?[a-z])(?=.*?[\d])[a-z\d]+\z/i.freeze validates_format_of :password, with: PASSWORD_REGEX, on: :create, message: 'には半角英字と半角数字の両方を含めて設定してください'開発環境でもエラーが発生することなく、テストも通ったため、本番環境にデプロイをしたところ「Sorry, something went wrong.」が表示されてしまいました。
原因
SecureRandom.alphanumeric
で英字のみのパスワードが発行されていたため。
そもそもSecureRandomはレファレンスでは以下のように説明がされています。安全な乱数発生器のためのインターフェースを提供するモジュールです。 HTTP のセッションキーなどに適しています。
そして
alphanumeric
はSecureRandomモジュールのメソッドの一種であり、ランダムな英数字を生成してくれます。しかし必ずしも英数字混合で生成されるわけではありません。コンソールpry(main)> SecureRandom.alphanumeric => "NNCMHbfUbHRQmbwW"確率は高くはないですが、上記のように英字のみのパスワードが生成されてしまうことがあります。
解決策
class Users::SessionsController < Devise::SessionsController # ゲストユーザーとしてログイン def new_guest user = User.find_or_create_by!(nickname: 'ゲストユーザー', email: 'user@example.com') do |user| user.password = SecureRandom.alphanumeric(10) + [*'a'..'z'].sample(1).join + [*'0'..'9'].sample(1).join end sign_in user redirect_to root_path, notice: 'ゲストユーザーとしてログインしました。' end end力技になってしまいましたが、
[*'a'..'z'].sample(1).join + [*'0'..'9'].sample(1).join
とすることでパスワードの最後に必ず英字+数字の2文字が入るようにして英字のみor数字のみのパスワードが生成されないようにしました。
- 投稿日:2020-11-17T01:23:27+09:00
【Rails】カラムの追加・データ型/カラム名の変更
はじめに
カラムの追加・カラム名、テータ型の変更をする手順として代表的なのが、
1⃣rails db:migrate:status
で現状の確認
2⃣マイグレーションファイルの作成or追加コマンドをターミナルに入力(行いたい操作でコマンドは異なります)
3⃣マイグレーションファイルに変更内容を記述(追加の場合は内容の確認だけになります)
4⃣rails db:migrate
だと思います(違ってたらすみません)。もちろんこれも正解だと思います。(rails db:rollback使うこともあります)毎度コマンドを調べたり誤字があるとテーブルが謎に消えてしまうことがあるかと思います、、、レコードの内容が消えていいような場合は楽な方法があるそうで、、(ご存じの方はすみません)
※レコードの内容が少なかったり消えても良い状態、seeds.rbで必要なデータを作成しているとき向けの内容です。
上記の楽な方法(複数同時変更も可能)
1⃣
rails db:migrate:status
で現状の確認(問題なければ2⃣へ)
2⃣既に作成してあるマイグレーションファイルの変更したい部分を直接書き換えます(追加の場合は書き加えてください)
3⃣rails db:migrate:reset
を実行して完了
schema.rbを確認してみてください!
rails db:migrate:reset
を行うことで、既存のマイグレーションファイルを全て利用して再度テーブルを作成しなおします。なので、書き換えた部分や追記した部分も全て反映されます(誤字でerrorが出ても大体直ります)
ただし、場合によって使い分ける必要があるのでレコードを削除したくない場合は丁寧にコマンドを打ち込みましょう。例
1⃣状態確認
rails db:migrate:status
実行
コマンド実行時に出力された分だけマイグレーションファイルが存在して、この後の工程でそれらを一斉に再度migreateしなおすようなイメージになります。ターミナルvocstartsoft:~/environment/bookers2-task (master) $ rails db:migrate:status #ここで実行 database: /home/ec2-user/environment/bookers2-task/db/development.sqlite3 Status Migration ID Migration Name -------------------------------------------------- up 20200830060820 Devise create users #upなのかdownなのか状態が出てきます。 up 20200830062142 Create books #upは基本的にmigrate済みということになります up 20201101080413 Create book comments #以下省略2⃣マイグレーションファイルに追記・変更
追記・変更したい内容の含まれるマイグレーションファイルをdb/migrateフォルダ内から持ってきます。
今回は投稿に使うtitleカラムとbodyカラム共にデータ型がおかしいのでそこを修正します(booleanとかどんなミスだよ。)db/migrate/20201115102020_create_books.rbclass CreateBooks < ActiveRecord::Migration[5.2] def change create_table :books do |t| t.integer :title #←integerをstringにしたい t.boolean :body #←booleanをtextにしたい t.integer :user_id t.timestamps end end end書き換えます(追加したい場合は追記してください)
db/migrate/20201115102020_create_books.rb#↑省略 t.string :title #integer→string t.text :body #boolean→text #↓省略3⃣rails db:migrate:resetを実行して一気にDBを削除→作成します
ターミナル$ rails db:migrate:resetターミナルに普段のrails db:migrate時と同じような内容が出力されていればおそらく問題ありません。
schema.rbを確認してちゃんと変更・追加ができているか確認してみましょう。
くどいようですが、作成していたuser情報(名前とか)や投稿内容などは消えているはずなので、注意してください。おまけ
rails db:resetとrails db:migrat:resetの違いについて
どちらのコマンドもDBを削除して作成しなおすコマンドですが、大きな違いがあるようです。
rails db:reset
はDBをdropして、現在の scheme.rb をロードして DB を作り直します。db/migrate/~.rb は使われないようです。
rails db:migrate:reset
はDBをdropした後、通常通りのマイグレート(db:migrate)が行われます。つまりdb/migrate/~.rb が古い順から全て実行されます。
最後に上記の方法以外のカラム追加記事はこちら
参考にもなったし、逆にrails db:migrate:resetできなかったときの内容の記事はこちら
長くなりましたが、個人的にDB周りは意外とややこしいエラーが多い気がします、この記事で解決すると嬉しいです!(改善されない方はすみません、、)