- 投稿日:2020-11-15T23:35:05+09:00
【アウトプット】eachについて
eachについて
何をするものか
配列の中身などを繰り返しやってくれる働きをもつ
例)
<% @posts.each do |post| %>
postsは変数名 postはとりあえず配列の中身を入れるやつ
< div class = "posts-index-item">< % = post.content %>
ここはpostだけでも良い状況がある。
今回はターミナルでPost.allで配列を取っている(contentというカラム名にデータがある為)
< /div>
<% end %>例をあげたら余計にややこしくなったが繰り返しの処理を書くときにeachを書いたら
短く書けるということ。
- 投稿日:2020-11-15T22:23:39+09:00
【個人メモ】RailsアプリをAWSへデプロイする際につまづいたことまとめ
index
下記の記事の通りにRailsアプリをAWSへデプロイする際につまづいたことを個人的な備忘としてまとめました。
(下準備編)世界一丁寧なAWS解説。EC2を利用して、RailsアプリをAWSにあげるまで
https://qiita.com/naoki_mochizuki/items/f795fe3e661a3349a7ce
https://qiita.com/naoki_mochizuki/items/22cfbf4bf7ec95f6ac1c
https://qiita.com/naoki_mochizuki/items/814e0979217b1a25aa3e
https://qiita.com/naoki_mochizuki/items/5a1757d222806cbe0cd1RDSインスタンスが生成できない
RDSの設定をして、「データベースの作成」をクリックすると、こんなエラーが発生。
DB Subnet Group doesn't meet availability zone coverage requirement. Please add subnets to cover at least 2 availability zones. Current coverage: 1 (Service: AmazonRDS; Status Code: 400; Error Code: DBSubnetGroupDoesNotCoverEnoughAZs; Request ID: 3e87202c-e6b3-46dc-8396-47c64a2f0dd6)こちらの記事を参考にしました。
https://www.wantanblog.com/entry/2019/09/24/225020
どうやらサブネットグループを1つしか作っていなかったことが問題のようなので、「VPC」→「サブネット」→「サブネットの作成」から、下記設定でサブネットをもう一つ作成したところ、RDSインスタンスが作成できるようになった。
・VPC ・・・作成したVPCを選ぶ
・アベイラビリティーゾーン ・・・既に作成したサブネットと違う場所を指定する
・IPv4 CIDR ブロック ・・・10.0.1.0/24EC2へSSHでログインできない
*[ .ssh ] $: ssh -i mumu.pem ec2-user@54.92.121.123でEC2へログインしようとすると、しばらく待ってから接続エラーになりました。
こちらの記事を参考にしました。
https://xn--o9j8h1c9hb5756dt0ua226amc1a.com/?p=3583
EC2が配置されているサブネットのルートテーブルを確認したところ、外部への経路(送信先0.0.0.0/0、ターゲットigw-...)が設定されていませんでした(うっかり)
「VPC」→「ルートテーブル」→「該当のルートテーブルにチェック」→「ルートテーブル」→「ルートテーブルの編集」→「ルートを追加」で下記を追加したところ、EC2へSSH接続できるようになりました。
・送信先 ・・・ 0.0.0.0/0
・ターゲット ・・・igw-...(作成したインターネットゲートウェイ)公開鍵作成するときにssh-keygem not foundのエラー
タイポでした。
ssh-keygemではなくssh-keygenでした。rake secretでシークレットキーが作れない
下記のエラーが発生
$ rake secret You must use Bundler 2 or greater with this lockfile.こちらの記事を参考にしました。
https://programming-beginner-zeroichi.jp/articles/169$ gem install bundler $ bundle install $ bundle exec rake secretで解決しました。(※ちなみに筆者はアプリのデータベースをsqliteに設定していたため、「bundle install」でエラーが発生し、次の記事の手順を踏んでから、「bundle install」をしました。
bundle installしたとき、sqlite3をインストールしてくださいのエラー
MySQLをデータベースに使う予定なので、railsアプリのデータベースをsqliteからMySQLへ変更します。
こちらの記事を参考にしました。
https://note.com/itoa06/n/n31fe4f9cd6b9差分はこちら
/Gemfile-gem 'sqlite3', '~> 1.4' +gem 'mysql2', '>= 0.4.4'/config/database.yml
default: &default - adapter: sqlite3 + adapter: mysql2 + encoding: utf8mb4 pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> - timeout: 5000 + username: root + password: + host: localhost development: <<: *default - database: db/development.sqlite3 + database: hello_rails_development test: <<: *default - database: db/test.sqlite3 + database: hello_rails_test production: <<: *default - database: db/production.sqlite3 + database: hello_rails_production + username: hello_rails + password: <%= ENV['HELLO_RAILS_DATABASE_PASSWORD'] %>Failed to start mysqld.service: Unit not found.
MySQLを立ち上げようとしたところエラーが発生しました
sudo service mysqld start Redirecting to /bin/systemctl start mysqld.service Failed to start mysqld.service: Unit not found.こちらの記事を参考にしました。
https://qiita.com/hamham/items/fd77bb0bb167a150dc8e#mysql57%E3%81%AE%E5%B0%8E%E5%85%A5@MurakamiKazutaka さんがコメントで書かれていましたが、Amazon Linux2ではyumでmysqlをインストールしようとするとmariaDBをインストールしようとするらしいです。
$ yum -y install http://dev.mysql.com/get/mysql57-community-release-el7-11.noarch.rpm $ yum -y install mysql mysql-community-server $ mysqld --version mysqld Ver 5.7.23 for Linux on x86_64 (MySQL Community Server (GPL)) $ cd /var/www/rails/アプリ名 $ sudo service mysqld startで無事解決しました。
と思ったら、無事ではありませんでした。この場合、MySQLが勝手にrootユーザーのパスワードを作成してしまうので、@hat_log さんがおっしゃっているように、パスワードをdatabase.ymlに記載する必要があります。
https://qiita.com/Dough/items/7493ad374a51b24abb58$ sudo cat /var/log/mysqld.log | grep 'temporary password' [Note] A temporary password is generated for root@localhost: XXXXXX $ mysql -u root -p Enter password: XXXXXX mysql> set password for root@localhost=password('passwordPASSWORD@999');次にdatabase.ymlにパスワードを記載
production: <<: *default database: mumu_production username: root password: passwordPASSWORD@999NoMethodError (undefined method `deep_symbolize_keys' forのエラー
データベース作成時にエラーが発生しました。
$ rake db:create RAILS_ENV=production ... NoMethodError (undefined method `deep_symbolize_keys' for....ymlファイルの書式が間違っているということで、おそらくインデントのスペースが2個になっていないのだろうと思い探しました。
そしたら、config/secrets.ymlでキー名secret_key_base:を書いていませんでした(うっかり)。誤り↓
production: (生成したシークレットキー)正しい方↓
production: secret_key_base: (生成したシークレットキー)Job for nginx.service failed because the control process exited with error codeのエラー
Nginxを起動しようとしたときにエラーが発生
$ sudo service nginx start Redirecting to /bin/systemctl start nginx.service Job for nginx.service failed because the control process exited with error code. See "systemctl status nginx.service" and "journalctl -xe" for details.こちらの記事を参考にしました。
https://qiita.com/shota0701nemoto/items/a6929ef6f396cf3bede4$ sudo nginx -t nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: [emerg] open() "/var/www/rails/[誤ったアプリ名]/log/nginx.error.log" failed (2: No such file or directory) nginx: configuration file /etc/nginx/nginx.conf test failedエラーの内容は人によって違うと思います。自分は"/var/www/rails/[誤ったアプリ名]/log/nginx.error.log"が無いということだったので、ファイルへ移動したところ、
$ cd /var/www/rails/[誤ったアプリ名]/log -bash: cd: /var/www/rails/[誤ったアプリ名]/log: No such file or directoryと言われ、よく見るとアプリ名のハイフン(-)とアンダーバー(_)を書き間違えていることに気付きました。
$ vim config/unicorn.conf.rb $ cd /etc/nginx/conf.d/ $ sudo vim mumu.confで誤った箇所を修正しました。
$ cd /var/www/rails/[アプリ名]/ $ sudo service nginx startで無事に起動しました。
We're sorry, but something went wrong. パート1
Nginxを起動した後に、EC2のIPアドレスへChromeでアクセスしてみると、
We're sorry, but something went wrong.となった。
RDSを一旦停止したせいかと思い、$ sudo service mysqld startMySQLを起動しましたが、変化無し。
$ less log/production.logでRailsのログを確認したところ(一番下の方が最新の情報)、
ActionView::Template::Error (The asset "application.css" is not present in the asset pipeline.のエラーが出ていたので、こちらの記事を参考にconfig/envitonments/production.rbを書き換え。
https://kanoe.studio/archives/791# Do not fallback to assets pipeline if a precompiled asset is missed. config.assets.compile = trueアプリケーションサーバーを再起動して、
(こちらの記事を参考にしました
https://qiita.com/takuyanagai0213/items/259ca105e35f6eb066d6
)$ ps -ef | grep unicorn | grep -v grep takuya 2460 1 0 3月11 ? 00:00:04 unicorn_rails master -c /var/www/rails/myapp/config/unicorn.conf.rb -D -E production takuya 2465 2460 0 3月11 ? 00:00:05 unicorn_rails worker[0] -c /var/www/rails/myapp/config/unicorn.conf.rb -D -E production takuya 2467 2460 0 3月11 ? 00:00:04 unicorn_rails worker[1] -c /var/www/rails/myapp/config/unicorn.conf.rb -D -E production $ kill 2460 $ unicorn_rails -c /var/www/rails/myapp(自分のアプリ名)/config/unicorn.conf.rb -D -E production再びEC2のIPアドレスにアクセスしましたが、相変わらず「We're sorry, but something went wrong.」
次に続く(まだエラーあんのかよ。。。)We're sorry, but something went wrong. パート2
引き続き、再度、ログを確認したところ、
$ less log/production.log ActionView::Template::Error (Webpacker can't find application in /var/www/rails/hello-rails/public/packs/manifest.json. Possible causes: 1. You want to set webpacker.yml value of compile to true for your environment unless you are using the `webpack -w` or the webpack-dev-server. 2. webpack has not yet re-run to reflect updates. 3. You have misconfigured Webpacker's config/webpacker.yml file. 4. Your webpack configuration is not creating a manifest.のエラーが発生していました。
ちょっと何言ってるかわからないです
こちらの記事を参考にしました。
https://qiita.com/natecotus/items/a2bd9f3ebd5b1866d48e$ rm -rf bin/webpack* $ rails webpacker:install Webpacker requires Node.js >= 8.16.0 and you are using 6.17.1 Please upgrade Node.js https://nodejs.org/en/download/またしてもエラーが発生。Node.jsのversionが古いみたいです。
こちらの記事を参考にしました。
https://qiita.com/paranishian/items/bddaed7c3aacedb11967$ git clone git://github.com/creationix/nvm.git .nvm $ . ~/.nvm/nvm.sh $ nvm install $ curl -o- -L https://yarnpkg.com/install.sh | bash $ source ~/.bashrc $ yarn -vそれでは、改めまして〜
$ rails webpacker:install [Ynaqdhm] Y [Ynaqdhm] Y $ RAILS_ENV=production bundle exec rails webpacker:compileコンパイルができたようなので、UnicornとNginxを再起動します。
$ ps -ef | grep unicorn | grep -v grep $ kill [プロセスID] $ unicorn_rails -c /var/www/rails/[アプリ名]/config/unicorn.conf.rb -D -E production $ sudo nginx -s reloadこれで、IPアドレスでつないでみたら。。。
やった表示された!
- 投稿日:2020-11-15T22:13:35+09:00
railsのサーバーが起動できない時の対処法
はじめに
初めまして。
私は未経験エンジニアとして転職するべく、1週間前にMacBookProを購入し、Progateで学習をしている者です。
早速ですが、Ruby on Railsの環境構築をする時に詰みました。rails s が起動しない
Progateに言われるがままAtom, Homebrew, rbenv, Railsをインストールし、後はローカルでサーバーを建てるだけ。
しかし、rails sと入力すると
Could not find gem 'rails (~> 6.0.3, >= 6.0.3.4)' in any of the gem sources listed in your Gemfile. Run `bundle install` to install missing gems.何のことやら。
とりあえずbundle installと入力しろと書いてあるので実行してみます。
An error occurred while installing bindex (0.8.1), and Bundler cannot continue. Make sure that `gem install bindex -v '0.8.1' --source 'https://rubygems.org/'` succeeds before bundling.初学者の心を折りに来ています。
Google先生に聞くこと数時間、sudo gem install bindex -v '0.8.1'これでパスワードを入力してインストールすれば良いみたいです。
さて、再度bundle installを実行すると、An error occurred while installing msgpack (1.3.3), and Bundler cannot continue. Make sure that `gem install msgpack -v '1.3.3' --source 'https://rubygems.org/'` succeeds before bundling.またですか。
しかし、同じようにsudo gem install msgpack -v '1.3.3'でインストールしていきます。
このあともbundle installを実行しては足りないものをインストールする作業を繰り返していき、最終的にはrails sが実行できるようになりました。おわりに
私のように出鼻をくじかれている方の助けになれば幸いです。
- 投稿日:2020-11-15T22:13:35+09:00
Railsのサーバーが起動できない時の対処法
はじめに
初めまして。
私は未経験エンジニアとして転職するべく、1週間前にMacBookProを購入し、Progateで学習をしている者です。
早速ですが、Ruby on Railsの環境構築をする時に詰みました。rails s が起動しない
Progateに言われるがままAtom, Homebrew, rbenv, Railsをインストールし、後はローカルでサーバーを建てるだけ。
しかし、rails sと入力すると
Could not find gem 'rails (~> 6.0.3, >= 6.0.3.4)' in any of the gem sources listed in your Gemfile. Run `bundle install` to install missing gems.何のことやら。
とりあえずbundle installと入力しろと書いてあるので実行してみます。
An error occurred while installing bindex (0.8.1), and Bundler cannot continue. Make sure that `gem install bindex -v '0.8.1' --source 'https://rubygems.org/'` succeeds before bundling.初学者の心を折りに来ています。
Google先生に聞くこと数時間、sudo gem install bindex -v '0.8.1'これでパスワードを入力してインストールすれば良いみたいです。
さて、再度bundle installを実行すると、An error occurred while installing msgpack (1.3.3), and Bundler cannot continue. Make sure that `gem install msgpack -v '1.3.3' --source 'https://rubygems.org/'` succeeds before bundling.またですか。
しかし、同じようにsudo gem install msgpack -v '1.3.3'でインストールしていきます。
このあともbundle installを実行しては足りないものをインストールする作業を繰り返していき、最終的にはrails sが実行できるようになりました。おわりに
私のように出鼻をくじかれている方の助けになれば幸いです。
- 投稿日:2020-11-15T21:49:36+09:00
Ruby on Railsの開発環境をDockerで構築する方法(Rails 6.x)
Docker公式手順を参考に、Rails 6系で Dockerの開発環境を構築する手順を詳解します。
各コマンドの詳しい解説は私が以前に書いた Rails5系での手順 で解説しているので、本記事では割愛します。
Rails プロジェクトを作成
$ docker-compose run web rails new . --force --no-deps --database=mysqlコンテナをビルド
$ docker-compose buildデータベースファイルの修正
上記実行後、config/database.yml ファイルを下記の通りにまるっと修正します。
config/database.ymldefault: &default adapter: mysql2 encoding: utf8mb4 pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> username: root password: host: localhost development: <<: *default database: myapp_development host: db username: root password: password test: <<: *default database: myapp_test host: db username: root password: passwordデータベースを作成
$ docker-compose run web rails db:createWebpackerをインストール
Rails 6系から Webpacker が必要となっているため、webサーバのコンテナにwebpackerをインストールします。
$ docker-compose run web rails webpacker:installdocker-compose でコンテナを起動
$ docker-compose up -dアクセスして起動を確認
ブラウザから localhost:3000 にアクセスし、Rails の初期画面が表示されることを確認しましょう。
コンテナを停止
検証を終え、コンテナを停止させるときも docker-compose を使います。
$ docker-compose down
- 投稿日:2020-11-15T21:24:17+09:00
TECH CAMP (エンジニア転職)6週目の学習内容の振り返り
火曜日から本格的に最終課題の学習にはいったのですが、火曜日から日曜日までで6割ぐらいは進めた(?)と思います。この調子ですすめて22日までは必要な作業すべてを終わらせたいですね。それが終われば自分のポートフォリオを進めれる、、、ぐへへへ(変人感)
復習に入っていきたいところですがその前に、最終課題とはなんなのか、どんなことをやるのか少し話していきたいと思います。最終課題ではメルカリのようなフリマアプリの作成を通してサーバーサイドの実装を行っていきます。フロントサイドに関してはすでに用意されているのでやらなくても大丈夫です。この最終課題と今までの学習内容の違いはなにかというと、目標を達成(機能実装)するための手順を自分で考えないといけないところです。今までのカリキュラムだと、テキストに沿って決められたことをこなしていけばよかったのですが、最終課題においては機能実装を行うためにどのような工程が必要か自分で道筋を立てながら学習していくことになります。
例えば、ユーザー登録機能を作るならカラムの洗い出しとDB作成、必要なアクションの定義などなどがあります。最終課題以前だとカリキュラムに書かれたそれらの項目をこなしていくことで実装できるのですが、今回だと実装のための過程が明示されていないのでプロセスを考えないといけないという難しさがあります。私は今の所は大丈夫ですが、終盤にどうなるかが心配です。(笑)
ではでは、今週の学習内容をざざっと振り返ろうかなと思います。11/10~11/15までの進捗状況
・herokuへのデプロイ
・Basic認証の導入
・テーブル read.meの作成
・ユーザー管理(登録 ログイン ログアウト)機能の実装
・商品を出品機能の実装
・トップページに商品を表示する機能の実装herokuへのデプロイについて
これと認証に関してはやることがそう多くないですね
heroku内にアプリケーション作成をして、mysqlを作成、アプリケーションの情報をgitからプッシュしてマイグレーションを実行することでデプロイが可能となります!Basic認証について
Basic認証はauthenticate_or_request_with_http_basicメソッドを用いることでidとpasswordを要求することができます。ここで注意すべき点は、環境変数にidとpasswordを入れることです。コントローラーにそのまま設定してたらどちらもgithubで漏洩してしまうので。テーブル設計 Read.meの作成
それぞれのテーブルに必要なカラムを洗い出します。難しいことはないですが、read.meのバー(|や-)を揃えるのがかなり面倒ですユーザー管理機能の実装
deviseの導入と正規表現の導入がかなり苦戦しました。
passwordだと半角英数字混合6文字以上というのがあったり、半角カタカナ限定だったりと、、、あとちゃんと機能しているか確認のためテストコードを書くのですが、100行以上かくはめになりました。がんばったなぁ()商品を出品機能の実装
ここでは商品の状態や発送元、送料の負担などをプルダウン機能を用いて実装していくのですが、結構調べるのに時間がかかってしまいました。商品の値段に下限と上限があり、そのバリデーションをどうかけるのか全くわからなかったですね。自分のググり力が上がる食べ物があったらそれを主食にしたいです。トップページに商品を表示する機能の実装
トップページに登録された商品が新しいものから上に表示されるように実装を行いました。
横並びにするのに試行錯誤して2時間ほどかかってしまいました。
- 投稿日:2020-11-15T20:43:01+09:00
【Rails】テーブルのカラム名の変更方法
テーブルのカラム名の変更方法について
Railsでテーブルのカラム名の変更方法についてまとめました。
ステップとしては下記の通りです。① migrationファイル作成
② migrationファイルの編集
③ データベースへ反映今回は下記のようにカラム名を変更します。
変更前
wheather
変更後
weather
モデル名 カラム名(変更前) カラム名(変更後) users wheather weather ① migrationファイル作成
まずはカラム名を変更するためのmigrationファイルを作成します。
$rails generate migration rename_【変更前のカラム名】_column_to_【モデル名(複数形)】
今回は
$rails generate migration rename_wheather_column_to_users
と記述する。② migrationファイルの編集
/db/migrate
に新しいファイルが作成されるので、change
メソッドを追加し、そこに変更したいカラム名を記述する。今回作成されたファイル:
20201115004326_rename_wheather_column_to_users.rb
*作成日によって数字の部分は変わります。下記のように記述する。
/db/migrate/20201115004326_rename_wheather_column_to_users.rbclass RenameWheatherColumnToUsers < ActiveRecord::Migration[6.0] def change rename_column :モデル名, :カラム名(変更前), :カラム名(変更後) end end今回の場合は下記の通り記述。
/db/migrate/20201115004326_rename_wheather_column_to_users.rbclass RenameWheatherColumnToUsers < ActiveRecord::Migration[6.0] def change rename_column :users, :wheather, :weather end end③ データベースへ反映
最後に、データベースへ反映し、カラム名の変更は完了。
$rails db:migrate
以上です。
weather
をwheather
と書き間違えたばかりに、このような作業が発生してしまいました(笑)
皆様はくれぐれもスペルミスの無いようにお気をつけください☆
- 投稿日:2020-11-15T20:12:29+09:00
[初心者]一気に複数行のインデント修正ができるワザ「矩形選択(ブロック選択)」について
はじめに
Railsなどで、コードを打ち終わった後に、全体のバランスを整えたいと思う時ってありますよね。
コードを考えるのに夢中になってしまい、見返してみたら、インデントがバラバラで、めちゃくちゃ見にくいバランスになってしまっていたり。
また、複数の要素をまたぐ形で、親要素を後から追加しようと思って、子要素の全ての行に対して、1スペース分のインデントを空けなければならない時もあると思います。
プログラミング駆け出しの私は、インデントを空ける為に、ひたすらTABキーを打ち続けていました。そんな中、「矩形選択」なるワザを知ってから、世界が変わりました。
知っている方も多いと思いますが、もし使っていないor知らない方がいたら、非常にもったいないと思いますので、この便利な技を共有させてもらいます。こんな時ってどうしてますか?
次のコードをご覧ください。
deviseを利用して、ユーザーの新規登録画面を作成したコードです。
これだけで、機能としては利用できるのですが、見た目が美しくありません。Bootstrapを利用して、バランスを整えたいと思い、全体を
<div class="container">
<div class="row">
で囲もうと思います。qiita.rb<div class="container" > <div class="row" > ------------- ←ここに上のコードを入れ込みたい!!! </div > </div >そうですね。
間に入れるだけならそれで良いのですが、リーダブルコードを意識するなら、全ての行にインデントを空ける方が美しくなりそうです。でも、面倒ですよね。
分かります。
そんな時に、利用できるのがこの「矩形選択」です!矩形選択(ブロック選択)とは?
非常に便利な機能の使い方は簡単です。
macOSの場合、キーボードによる矩形選択のコマンド開始する箇所をクリックしておく [Shift]+[Option]+[Command]+矢印キー そのままドラッグこれだけです。
もっと分かりやすく説明すると、
このように、開始する箇所をクリックしておきます。
そして、↑のコマンドを打ち込むと、
何と便利なことに、<div class="field">
全体を囲むように、ポインタが拡大されました。
ここまで出来ると、もう簡単です。
見やすいように、↑の部分だけインデントを空けると、こんな感じになります。
はい、メチャクチャ便利です。矩形選択の他の使い方
もちろん、スペースを空けるだけじゃなく、文字を打つことも出来ます。
一度文字を打ち込むだけで、矩形選択した他の行にも同じ文字を打つことができました。
他にも色々と応用した使い方があるので、興味がある方は調べてみてください!おわりに
プログラミングの世界には、このように作業時間を縮小できるような小ワザがたくさんあると思います。
全てを一気に覚えるのは難しいですが、一つ一つ体に馴染ませていけば、非常にスマートなプログラマーになれる日が近づくかもしれません!
- 投稿日:2020-11-15T19:38:52+09:00
[Rails][ jQuery]フォームの入力ならびに選択が完了するまで送信ボタンを押せないようにする。
はじめに
今回は、jQueryを使って、フォームの入力ならびに選択が完了するまで送信ボタンが押せないように設定していきます。
完成イメージ
記事を書いた目的
情報の共有ならびに、自身の備忘録として執筆する。
導入した目的
誤送信を防ぎ、ユーザビリティを向上させるため。
環境
MacOS 10.15.7
ruby 2.6.5
Ruby on Rails 6.0.0前提条件
- jQueryが導入済みであること。
- 画像の複数枚投稿機能を実装している。
記事執筆者の状況
テーブル
Userテーブルが「投稿者」、Postテーブルが「投稿」、Imageテーブルが「投稿画像」、Prefectureテーブルが「都道府県データ」、Categoryテーブルが「投稿カテゴリー」となります。
PrefectureテーブルとCategoryテーブルはseedデータを活用しております。
コントローラー
新規投稿機能はposts_controller.rbが担当しております。
posts_controller.rbclass PostsController < ApplicationController def new @post = Post.new @post.build_spot @post.images.build() end def create @post = Post.new(post_params) if @post.save redirect_to root_path, notice: "投稿が完了しました" else flash.now[:alert] = "必須項目を入力してください" @post.images.build() render :new end end ...下記一部記述を省略 . . . private def post_params params.require(:post).permit(:title, :content, :prefecture_id, :category_id, images_attributes: [:id, :image, :_destroy]).merge(user_id: current_user.id) endそれでは作業していきましょう。
①-1 new.html.erbを作成する
まずはフォームをhtmlで作成していきます。
new.html.erb<%= form_with(model: @post, local: true, multipart: true) do |form| %> <ul class='formSpace'> <li class="prefecture"> <label class="labelName" for="Prefecture">Prefecture:</label> <%= form.collection_select :prefecture_id, Prefecture.all, :id, :name, {include_blank: '選択してください'}, {class: "prefecture__input", id: 'input01'} %> </li> <li class="category"> <label class="labelname" for="category">Category:</label> <%= form.collection_select :category_id, Category.all, :id, :name, {include_blank: '選択してください'}, {class: "category__input", id: 'input02'} %> </li> <li class="title"> <label class="labelName" for="titleSpace">Title:</label> <%= form.text_field :title, class: 'title__input', id: "input03", placeholder: "タイトルを入力してください" %> </lil <li class='newImage'> <label class="labelName" for="imageSpace">Photo:</label> <div class="prevContent"> </div> <div class="labelContent"> <label class="labelBox" for="post_images_attributes_0_image"> <div class="labelBox__text-visible"> クリックしてファイルをアップロード(最大5枚) </div> </label> </div> <div class="hiddenContent"> <%= form.fields_for :images do |i| %> <%= i.file_field :image, class: "hiddenField", id: "post_images_attributes_0_image", name: "post[images_attributes][0][image]", type: "file" %> <%= i.file_field :image, class: "hiddenField", id: "post_images_attributes_1_image", name: "post[images_attributes][1][image]", type: "file" %> <%= i.file_field :image, class: "hiddenField", id: "post_images_attributes_2_image", name: "post[images_attributes][2][image]", type: "file" %> <%= i.file_field :image, class: "hiddenField", id: "post_images_attributes_3_image", name: "post[images_attributes][3][image]", type: "file" %> <%= i.file_field :image, class: "hiddenField", id: "post_images_attributes_4_image", name: "post[images_attributes][4][image]", type: "file" %> <% end %> </div> </li> <li class='content'> <label class="labelName" for="contentSpace">Content:</label> <%= form.text_area :content, class: 'content__input', id: "input05", placeholder: "コメントを入力してください" %> </li> </ul> <div class='send'> <%# <%= form.submit "送信中", class: 'send__btn', id: 'sending', value: "投稿する" %> <input type='submit' id='sending' class='send__btn' value='投稿する'> </div> <% end %>続いてscssを記述します。
new.scss.formSpace { height: auto; } .labelName { color: #000000; } // 都道府県================================================================ .prefecture { height: auto; width: auto; margin-top: 1vh; font-size: 1.5vh; line-height: 1.5; color: #fff; &__input { width: auto; border: 1px solid #ccc; background-color: #fff; border-radius: 5px; text-align: center; color: #000000; } } // カテゴリー============================================== .category { height: auto; width: auto; margin-top: 1vh; font-size: 1.5vh; line-height: 1.5; color: #fff; &__input { width: auto; border: 1px solid #ccc; background-color: #fff; border-radius: 5px; color: #000000; } } //Title=================================================================== .title { height: auto; width: auto; margin-top: 1vh; font-size: 1.5vh; line-height: 1.5; color: #fff; &__input { width: 30vw; border-radius: 5px; border: 1px solid #ccc; background-color: #fff; color: #000000; margin-left: 25px; } } //Image====================================================================== .newImage { display: block; margin: 16px auto 0; display: flex; flex-wrap: wrap; cursor: pointer; } .imageLabelName { color: #fff; margin-right: 25px; } .prevContent { display: flex; } .previewBox { height: 162px; width: 112px; margin: 0 15px 10px 0; } .upperBox { height: 112px; width: 100%; img { width: 112px; height: 112px; } } .lowerBox { display: flex; text-align: center; } .deleteBox { color: #1e90ff; width: 100%; height: 50px; line-height: 50px; background: #f5f5f5; display: flex; justify-content: center; align-items: center; cursor: pointer; } .imageDeleteBtn { background-color: #f5f5f5; line-height: 4vh; height: 4vh; width: 60px; } .imageDeleteBtn:hover { color: rgba($color: #1e90ff, $alpha: 0.7); } //投稿クリックエリアのCSS .labelContent { margin-bottom: 10px; width: 620px; .labelBox { display: block; border: 1px dashed #ccc; position: relative; background: #f5f5f5; width: 100%; height: 162px; cursor: pointer; &__text-visible { position: absolute; top: 50%; left: 16px; right: 16px; text-align: center; font-size: 14px; line-height: 1.5; font-weight: bold; -webkit-transform: translate(0, -50%); transform: translate(0, -50%); pointer-events: none; white-space: pre-wrap; word-wrap: break-word; } } } //file_fieldのcss .hiddenContent { .hiddenField { display: none; } .hidden-checkbox { display: none; } } //コメント==================================================================== .content { display: flex; height: auto; width: auto; margin-top: 5px; line-height: 1.5; font-size: 1.5vh; &__input { height: 15vh; width: 40vw; border-radius: 5px; color: #000000; border: 1px solid #ccc; background-color: #fff; margin-left: 0px; padding: 1vh; } } //SENDボタン========================================================================= .send { display: flex; justify-content: center; &__btn { height: 5vh; width: 25vw; margin: 50px 0; border-radius: 20px; background-color: #87cefa; border: none; box-shadow: 0 0 8px gray; color: #ffffff; line-height: 1.5; font-size: 2vh; font-weight: bold; -webkit-transition: all 0.3s ease; -moz-transition: all 0.3s ease; -o-transition: all 0.3s ease; transition: all 0.3s ease; } :hover { background-color: #00bfff; } .send__btn[disabled] { background-color: #ddd; cursor: not-allowed; } }ポイント
今回、JavaScriptにてフォームが入力または選択されているかどうかを状態管理するために
<%= form.collection_select :prefecture_id, Prefecture.all, :id, :name, {include_blank: '選択してください'}, {class: "prefecture__input", id: 'input01'} %>という風に、
id: 'input01'
という形でidを指定しています。このように、各フォームにidを指定していくのですが、idは同名のものを使い回しすることが不可のため、今回は
id='input02'
、id='input03'
・・・という形でidを順番に振っています。
(クラス名を統一して、使い回すのもありですが、今回は省略します。)SCSSについては、ボタン(class:send__btn)が有効・無効の状態別でボタンの色を変更する記述をしております。ボタンの状態管理は後述するdistabledという値を使って管理します。
無効の場合にはdistabledという値が要素に付与されることになるので、scssの方で.send__btn[disabled] { background-color: #ddd; cursor: not-allowed; }という記述をして、ボタンが無効状態の場合は色を灰色にして、カーソルを無効にしています。
①-2 submit.jsに処理を記述
あとはjsファイルにフォームの入力・選択がされているかどうかを判定し、送信ボタンの有効・無効を切り替える処理を記述していきます。
今回はsubmit.jsというファイルに記述していきます。
submit.js// フォームを入力・選択するまで送信ボタンが押せないようにする============================================= $(function() { //最初に送信ボタンを無効にする $('#sending').prop("disabled", true); //idに「input」と設定している入力欄の操作時 $("[id^= input],#post_images_attributes_0_image").change(function () { //入力欄が空かどうか判定を定義するために、sendという変数を使ってフォームの中身の状態管理を行う。 let send = true; //id=input~と指定している入力欄をひとつずつチェック&画像(インデックス番号が0番の画像)をチェックする $("[id^= input],#post_images_attributes_0_image").each(function(index) { //フォームの中身(値)を順番に確認し、もしフォームの値が空の時はsend = false とする if ($("[id^= input],#post_images_attributes_0_image").eq(index).val() === "") { send = false; } }); //フォームが全て埋まっていたら(send = trueの場合) if (send) { //送信ボタンを有効にする $('#sending').prop("disabled", false); } // フォームが一つでも空だったら(send = falseの場合) else { //送信ボタンを無効にする $('#sending').prop("disabled", true); } }); });ポイント
最初に送信ボタン
<input type='submit' id='sending' class='send__btn' value='投稿する'>に対して、
prop(disabled, false)
として、ボタンを無効化しています。propメソッドは指定した属性に値を設定する役割を持っています。
distabledとは指定したHTML要素を無効化できる属性のことです。
propメソッドと組み合わせて使うことで
prop( ‘disabled’, true)」・・・要素を無効化
prop( ‘disabled’, false)」・・・要素を有効化
というふうに使用することができます。参照:
propメソッド・・・http://js.studio-kingdom.com/jquery/attributes/prop
distabled・・・https://persol-tech-s.co.jp/hatalabo/it_engineer/463.html#disabled次に、
$("[id^= input],#post_images_attributes_0_image").change(function ()と記述しています。
「
[id^= input]
と#post_images_attributes_0_image
の値が変化した時に、イベントが発火する」という記述になります。注目いただきたいのは
javascript
[id^= input]
の部分です。
こちらはjQueryの属性を使った指定方法を採用しています。
指定方法には大まかに分けて4つあります。
- 前方一致
- 後方一致
- 部分一致
- 否定
「前方一致」は「属性 ^= 属性名」のように「^」を追加するだけで、属性名の先頭部分の文字列が一致するすべての要素を取得することができます。
今回の場合、
[id^= input]
とすることで、id="input01", id="input02,・・・ id="input05の要素、つまりid名にinputと命名されている要素を全て取得することができます。なお、jQueryの属性を使った指定方法についてはこちらの記事を参考にさせていただきました。前方一致指定意外にも知りたい方はご覧いただければと思います。
続いて、
let send = true;については、入力欄が空かどうか判定するために、sendという変数を用いてフォームの状態管理を行うために記述しています。trueの場合は、フォームが全て埋まっている状態を表します。
$("[id^= input],#post_images_attributes_0_image").each(function(index) { //フォームの中身(値)を順番に確認し、もしフォームの値が空の時はsend = false とする if ($("[id^= input],#post_images_attributes_0_image").eq(index).val() === "") { send = false; } });については、eachメソッドを使って、idにinputと命名している要素を、要素の個数分に応じて取り出します。
取り出す際、eachメソッドの引数にコールバック関数を定義する必要があるので、「function(index)」と指定します。こうすることでindex番号を取得することができ、取り出した要素にそれぞれindex番号を振り分けます。今回の場合、イメージとしては
0 : id="input01"の要素 1 : id="input02"の要素 2 : id="input03"の要素 3 : id="input04"の要素 4 : id="input05"の要素このような形になるかと思います。
加えて、
#post_images_attributes_0_image
も対象のオブジェクトに加えております。正直なところ、
複数枚画像投稿する際、上手いidの設定、指定ができなくて、ここに加えております。
(他に上手い方法があれば教えていただけると助かります!)eachメソッドでインデックス番号と一緒に取り出したあと、
if ($("[id^= input],#post_images_attributes_0_image").eq(index).val() === "") { send = false; }の処理に移ります。
ここでは、取り出した要素1つ1つのフォームの中身が空なのかを検証しています。
1つ1つ検証するにあたり、eqメソッド
を使用しています。
eqメソッドとは現在マッチしている要素をインデックス番号でフィルタリングします。
(eqメソッド参照サイト)idにinputと命名されている要素は、eachメソッドでインデックス番号が0〜4が振られているので、順番にeqメソッドの引数にインデックス番号が入るイメージです。(例: eq(0),eq(1)...eq(4) )
フォームの値を取得するのはvalメソッドを使用します(valメソッド参照サイト)
〜〜 === ""
とは、「〜〜は空である」という意味を表します。1つ1つ取り出した要素を検証して、1つでもフォームの値が空の要素があれば、
send = false
を返します。最後の
//フォームが全て埋まっていたら(send = trueの場合) if (send) { //送信ボタンを有効にする $('#sending').prop("disabled", false); } // フォームが一つでも空だったら(send = falseの場合) else { //送信ボタンを無効にする $('#sending').prop("disabled", true); }の部分については、
if (send)
(if send ==trueという意味)の場合つまりフォームが全て埋まっている場合は、$('#sending').prop("disabled", false);
というふうに、送信ボタンを有効にして、押せる状態にしています。
else
(send == false)の場合つまりフォームが1つでも空だった場合は、$('#sending').prop("disabled", true);
という形で、送信ボタンを無効にして、押せない状態にします。①-3 完成
以上で完成です。
最後に
初学者のためまだまだ理解不足な部分も多く、今回の実装については正直、改善の予知が多くあると思いますので、もっと良い実装の方法等がありましたらご教示いただけますと幸いです。
また、この記事をご覧いただきましたら、LGTMもいただけるとすごく嬉しいです。何卒よろしくお願い致します。
- 投稿日:2020-11-15T19:05:12+09:00
【Ruby On Rails】ネストした状態で、link_toメソッド内Prefixの後に書く括弧内の記述
すみません。完全なる備忘録です。。笑
前提
show.html.erb<% @fuga_events.each do |event| %> ---------------------------------------- <ul> <div> <%= "名前:#{event.hoge.name}" %> </div> <div> <%= "アプリ:#{event.hoge.app_name}" %> </div> <div> <%= "開始日:#{event.started_at}"%> </div> <div> <%= "終了日:#{event.finished_at}" %> </div> <div> <%= "todo:#{event.todo}" %> </div> <div> <%= "場所:#{event.place}" %> </div> <div> <%= "見込:#{event.expected_reward}" %> </div> <div> <%= "報酬:#{event.reward}" %> </div> </ul> <div> <%= link_to "編集する", edit_hoge_fuga_event_path(), method: :get %> <%= link_to "削除する", hoge_fuga_event_path(), method: :delete %> </div> <% end %>今回は、()内に渡したいparameterのid(キー)に相当するものを記述したい。
上記では、次の様にhogeが親でfuga_eventsが子のネスト状態となっている。routes.rb(前略) resources :hoges, except: [:index] do resources :fuga_events, except: [:index] end (後略)実際に、link_toメソッド内に記述されているPrefix(パス)、今回で言うとedit_hoge_fuga_event_pathとhoge_fuga_event_pathの後の括弧内にはどの様な記述が必要となるでしょうか。
書き方
show.html.erb<%= link_to "編集する", edit_hoge_fuga_event_path(event.papa_id, event.id), method: :get %> <%= link_to "削除する", hoge_fuga_event_path(event.papa_id, event.id), method: :delete %>編集(editアクション)と削除(destroyアクション)機能には、それぞれ(event.papa_id, event.id)を記述しました。
考え方
私は赤色がネストの親、青色がネストの子を表していて、それぞれに対応するキーを記述する と考えました。
<%= link_to "編集する", edit_ hoge _ fuga_event _path ( event.papa_id , event.id ), method: :get %>
<%= link_to "削除する", hoge _ fuga_event _path ( event.papa_id , event.id ), method: :delete %>
気付き
決してこの様な書き方が必ずではありません。結局、考え方としては、パスの後に書く括弧内ではどの様なidが渡されるかを指定して書いてあげることが重要であると気がつきました。
- 投稿日:2020-11-15T18:51:56+09:00
【Rails API + Vue】Active Storageを使って画像をアップロード・表示する
バックエンドはRails、フロントエンドはVueといった構成のときにActive Storageを使って画像をアップロード・表示する方法を、プロジェクトを1から作りながらまとめます
ソースコードはGitHubで公開しています画像をアップロード・表示する処理の流れをざっくりと
- Vueで画像を選択して送信するための画面を作る
- 送信ボタンを押した時、画像をアップロードする処理を行うRails APIを呼び出す
- Railsは受け取った画像を
storage
ディレクトリに保存し、保存した画像のURLを返す- Vueで画像のURLを受け取り、表示する
Railsプロジェクトを作成する
↓のようなディレクトリ構成で作成していきます
rails-vue-file-uploader-sample └── backend # Railsプロジェクト └── frontend # VueプロジェクトまずはRailsプロジェクトをAPIモードで作成します
$ mkdir rails-vue-file-uploader-sample $ cd rails-vue-file-uploader-sample $ rails _6.0_ new backend --api $ cd backend $ rails db:createActive Storageを使えるようにする
$ rails active_storage:install $ rails db:migrateこれらを実行するとactive_storage_blobsとactive_storage_attachmentsという名前の2つのテーブルが作成されます
これらはActiveStorage::BlobとActiveStorage::Attachmentの2つのモデルで扱われます
- ActiveStorage::Blob:アップロードファイルのメタ情報を管理するためのモデル
- ActiveStorage::Attachment:主となるモデルとActiveStorage::Blobとの中間テーブルに相当するモデル
例えばPostモデルに画像を持たせる場合は次のような関係になります
モデルを作成する
titleとimageを属性に持つPostモデルを作成します
imageの型にはattachment
を指定します$ rails g model post title:string image:attachment $ rails db:migrateこれらを実行するとpostsテーブルが作成されます
マイグレーションファイルを見てみるとわかるのですが、postsテーブルにimageカラムは作られません
image属性の中身はActiveStorage::Blob及びActiveStorage::Attachmentに保存され、それを参照するようになります生成された
app/models/post.rb
を見ると、has_one_attached :image
が指定されています
この指定によって画像を参照できるようになりますapp/models/post.rbclass Post < ApplicationRecord has_one_attached :image endコントローラを作成する
$ rails g controller posts
app/controllers/posts.rbclass PostsController < ApplicationController def index render json: Post.all end def create post = Post.new(post_params) if post.save render json: post else render json: post.errors, status: 422 end end def destroy post = Post.find(params[:id]) post.destroy! render json: post end private def post_params params.permit(:title, :image) end endとりあえず普通に書きます
routesも設定しますconfig/routes.rbRails.application.routes.draw do scope :api do resources :posts, only: [:index, :create, :destroy] end end保存したファイルのURLを返すようにする
Postモデルに、紐づいている画像のURLを取得するメソッドを追加します
url_for
メソッドを使うためにRails.application.routes.url_helpers
をincludeする必要がありますapp/models/post.rbclass Post < ApplicationRecord include Rails.application.routes.url_helpers has_one_attached :image def image_url # 紐づいている画像のURLを取得する image.attached? ? url_for(image) : nil end endアクションで返すJSONに
image_url
の値を追加しますapp/controllers/posts.rbclass PostsController < ApplicationController def index render json: Post.all, methods: [:image_url] # ここを変更 end def create post = Post.new(post_params) if post.save render json: post, methods: [:image_url] # ここを変更 else render json: post.errors, status: 422 end end def destroy post = Post.find(params[:id]) post.destroy! render json: post end private def post_params params.permit(:title, :image) end end画像のURLを取得するために
config/environments/development.rb
に次の設定を追加する必要がありますconfig/environments/development.rbRails.application.configure do ... # これを追加 Rails.application.routes.default_url_options[:host] = 'localhost' Rails.application.routes.default_url_options[:port] = 3000 endVueとのAPI通信をするためにCORSの設定をしておきます
Gemfileのgem 'rack-cors'
のコメントを外してbundle install
し、config/initializers/cors.rb
を次のように書きますconfig/initializers/cors.rbRails.application.config.middleware.insert_before 0, Rack::Cors do allow do origins 'http://localhost:8080' resource '*', headers: :any, methods: [:get, :post, :put, :patch, :delete, :options, :head] end endVueプロジェクトを作成する
ここからはVueを書いていきます
まずはルートディレクトリに戻ってVueプロジェクトを作成します$ cd rails-vue-file-uploader-sample $ vue create frontend $ cd frontendvue createの設定は以下のように選択しました
? Please pick a preset: Manually select features ? Check the features needed for your project: Vuex, Linter ? Pick a linter / formatter config: Prettier ? Pick additional lint features: Lint on save ? Where do you prefer placing config for Babel, ESLint, etc.? In dedicated config files ? Save this as a preset for future projects? NoVuexストアを作成する
Vuexを次のように書きます
axiosを使用するのでインストールしておきます$ npm install --save axiossrc/store/modules/posts.jsimport axios from "axios"; const apiUrlBase = "http://localhost:3000/api/posts"; const headers = { "Content-Type": "multipart/form-data" }; const state = { posts: [] }; const getters = { posts: state => state.posts.sort((a, b) => b.id - a.id) }; const mutations = { setPosts: (state, posts) => (state.posts = posts), appendPost: (state, post) => (state.posts = [...state.posts, post]), removePost: (state, id) => (state.posts = state.posts.filter(post => post.id !== id)) }; const actions = { async fetchPosts({ commit }) { try { const response = await axios.get(`${apiUrlBase}`); commit("setPosts", response.data); } catch (e) { console.error(e); } }, async createPost({ commit }, post) { try { const response = await axios.post(`${apiUrlBase}`, post, headers); commit("appendPost", response.data); } catch (e) { console.error(e); } }, async deletePost({ commit }, id) { try { axios.delete(`${apiUrlBase}/${id}`); commit("removePost", id); } catch (e) { console.error(e); } } }; export default { namespaced: true, state, getters, mutations, actions };src/store/index.jsimport Vue from "vue"; import Vuex from "vuex"; import posts from "./modules/posts"; Vue.use(Vuex); export default new Vuex.Store({ modules: { posts } });画像をアップロードするコンポーネントを作成する
画像を選択して送信するフォームを表示するための
src/components/PostForm.vue
を作成しますsrc/components/PostForm.vue<template> <div> <h2>PostForm</h2> <section> <label for="title">title: </label> <input type="text" name="title" v-model="title" placeholder="title" /> </section> <section> <label for="image">image: </label> <input type="file" id="image" name="image" accept="image/png,image/jpeg" @change="setImage" /> </section> <section> <button type="submit" @click="upload" :disabled="title === ''">upload</button> </section> </div> </template> <script> import { mapActions } from "vuex"; export default { name: "PostForm", data: () => ({ title: "", imageFile: null }), methods: { ...mapActions("posts", ["createPost"]), setImage(e) { e.preventDefault(); this.imageFile = e.target.files[0]; }, async upload() { let formData = new FormData(); formData.append("title", this.title); if (this.imageFile !== null) { formData.append("image", this.imageFile); } this.createPost(formData); this.resetForm(); }, resetForm() { this.title = ""; this.imageFile = null; } } }; </script>選択された画像は
e.target.files
で取り出すことができます
POSTリクエストを送信するときはFormData
に必要な値をappendしたものをパラメータとして指定します画像を表示するコンポーネントを作成する
保存されている画像を取得して表示するための
src/components/PostList.vue
を作成しますsrc/components/PostList.vue<template> <div> <h2>PostList</h2> <div v-for="post in posts" :key="post.id"> <h3>{{ post.title }}</h3> <img :src="post.image_url" /> <br /> <button type="submit" @click="del(post.id)">delete</button> </div> </div> </template> <script> import { mapActions, mapGetters } from "vuex"; export default { name: "PostList", created() { this.fetchPosts(); }, computed: { ...mapGetters("posts", ["posts"]) }, methods: { ...mapActions("posts", ["fetchPosts", "deletePost"]), del(id) { this.deletePost(id); } } }; </script>
<img :src="post.image_url" />
でsrcに取得したURLを指定して表示させます最後にApp.vueを編集してコンポーネントを表示します
src/App.vue<template> <div id="app"> <PostForm /> <PostList /> </div> </template> <script> import PostForm from "./components/PostForm.vue"; import PostList from "./components/PostList.vue"; export default { name: "App", components: { PostForm, PostList } }; </script>完成
画像を選択してアップロードボタンを押すと、画像が保存されて表示されます
画像はbackend/storage
ディレクトリにバイナリ形式で保存されますソースコードはGitHubで公開しています
参考になれば嬉しいです
https://github.com/youichiro/rails-vue-file-uploader-sample参考
- 投稿日:2020-11-15T17:03:29+09:00
Ruby on Railsの開発環境をDockerで構築する方法
この記事では、Dockerを用いてRuby on Rails(以下、rails)の開発環境を構築する方法を紹介します。
通常、rails の初学者の方はマシンに直接インストールして開発環境を構築する人がほとんどだと思います。
しかし、環境の微妙な際(Rubyのバージョンや環境変数など)によってつまづいた場合、
そのトラブルシューティングには多くの時間を要してしまうかと思います。そこで、Dockerで仮想環境上にrailsの環境を構築してあげることで
どのマシン上の開発環境でも等しい動作を行うことができるため、環境の差異によるエラーを避けることができます。
環境をいじりすぎた時には最悪一度リセットを出来るというのもメリットですね。また、作成したアプリケーションを本番環境として外部へ公開しようとするときにも
開発環境と本番環境での違いを最小限に抑え、安定したアプリケーションとして稼働させることができます.
Dockerで仮想環境を作って公開するなんて難しそうと思うかもしれませんが、
最近ではAWSといったクラウドサービスでもDockerコンテナをそのまま実行できるECS等のサービスが整っているので
Dockerで作った環境を公開するのも楽になってきています。前提条件
マシンに Docker がインストールされていること
Docker をインストールしていない人は、公式サイトから Docker のインストーラをダウンロードしてインストールしておきましょう。
→Docker公式また、任意の作業フォルダを作成しておきます。
ここでは "rails-docker" というフォルダを作成し、その中で作業していくことにします。$ mkdir rails-docker $ cd rails-dockerDocker関連ファイルの準備
rails-docker 内に以下4つの空ファイルを準備しておきます。
VScode を利用しているなら新規ファイルの作成で作ったり、
Mac のターミナルから touch コマンドを使ってもよいです。. ├── Dockerfile # 新規作成 ├── Gemfile # 新規作成 ├── Gemfile.lock # 新規作成 └── docker-compose.yml # 新規作成Dockerファイルの解説
Dockerでは、Dockerfileというファイルに基づいてコンテナのビルドが行われ、
Docker Image(コンテナの雛形)が作成されることになります。まず Dockerfile の全文を記載します。
使用する Ruby のバージョンは 2.7.0 としています。FROM ruby:2.7.0 RUN apt-get update -qq && apt-get install -y build-essential nodejs RUN mkdir /app WORKDIR /app COPY Gemfile /app/Gemfile COPY Gemfile.lock /app/Gemfile.lock RUN bundle install COPY . /app箇条書きとはなりますが、それぞれの意味を解説します。
- FROM ruby:2.7.0
- 公開されている ruby インストール済みコンテナのうち、Rubyのバージョンが 2.7.0 であるものを引っ張ってきます。
- RUN apt-get update -qq && apt-get install -y build-essential nodejs
- Rails の実行に必要なパッケージをインストールしています
- RUN mkdir /app
- Rails のプロジェクトファイルを作成するアプリディレクトリを作成
- WORKDIR /app
- 作業用のディレクトリを指定
- COPY Gemfile /app/Gemfile
- 自分のマシン(PC)上にある Gemfile をコンテナの作業用ディレクトリに移動させ、コンテナから利用できるようにします
- Gemfile.lock も同様
- RUN bundle install
- Gemfileに記載されているgemを一括インストール
- COPY . /app
- Dockerファイルが置いてあるフォルダのファイルすべてをコンテナ内の app ディレクトリにコピー
- Rails の実行に必要なファイルをコンテナに含めるためにコピーしています
Gemfile の解説
Ruby では Gemfile というファイルで環境で使いたい gem を定義することができます。
gemとはRubyのライブラリのことをいいます。
gemはRubyGemsと呼ばれるRuby用のパッケージ管理システムで管理されており、RubyGemsが提供するgemコマンドを通じてインストール等ができます。source 'https://rubygems.org' gem 'rails', '5.2.1'また少し解説します。
- source 'https://rubygems.org'
- gem のダウンロード元を指定
- gem 'rails', '5.2.1'
- インストールする gem である rails というパッケージ名と、バージョンを指定
- 今回は rails のバージョンは 5.2.1 を指定しています
- どのバージョンを指定してもよいのですが rails 6 以降は webpacker, yarn が必要になるので注意してください
- ここでは簡単に検証するため rails 5 系を使用
Gemfile があるディレクトリで "bundle install" コマンドを実行すると、
Gemfile の定義に従って、定義した gem をインストールすることができます。先ほど解説した Docker ファイルでも、Gemfile をコンテナの作業用ディレクトリにコピーして
そのフォルダ内で bundle install を実行するように指定していましたね。Gemfile.lock の解説
実は Gemfile.lock は最初の時点では何も書き込む必要がありません。
これは直接編集するようなファイルではなく、
Gemfile に基づいて bundle install を実行した後に
インストールされた gem が Gemfile.lock に一覧として記述されるようになります。使い方としては、Gemfile.lock を参照すればインストールされている gem とバージョンが分かり、
Gemfile.lock から bundle install を実行することもできるので
完全に同じ環境をもう一度作りたいときや、多人数で開発をするときに Gemfile.lock が使われます。Gemfile だけでも良いのでは?と思った鋭い方に補足しますと、Gemfile では実はこういう書き方もできることが理由の一つになっています。
gem 'rails', '~> 5.2.1'この場合、bundle install でインストールされる rails のバージョンは 5.2.1 以上であるものの
実際にインストールできる(公開されている)バージョンはその時点によって変わってきます。しかし Gemfile.lock では Gemfile に従ってインストールしようとした時に実際にインストールされた gem 本体と具体的なバージョン、
付随して必要となるためにインストールされたパッケージまで記録されます。少し整理すると Gemfile は「アプリで必要な gem の一覧」であり、
一方の Gemfile.lock は「Gemfile に従った結果、実際にインストールした gem の情報」が記述されることになります。docker-compose.yml の解説
この yml ファイルは Docker Compose で使用されます。
Docker Composeは、複数のコンテナで構成されるアプリケーションについて
Dockerイメージのビルドや各コンテナの起動・停止といった管理を行うためのツールです。Docker Composeでは、Dockerビルドやコンテナ起動のオプションも含め、複数のコンテナのための定義を docker-compose.yml というファイルに記述し、
それを利用して Docker イメージのビルドやコンテナ起動をすることができます。開発環境のように、Rails をそれ単体で動かすならば Dockerfile だけでもよいのですが
Rails を動かすためのアプリケーションサーバに加え、
実際に公開するときにはインターネットからのアクセスを受け付けるための Web サーバや、
またデータを保存・処理するためのデータベースサーバも用意することがあります。docker-compose.ymlversion: '3' services: web: build: . command: bundle exec rails s -p 3000 -b '0.0.0.0' volumes: - .:/app ports: - 3000:3000 depends_on: - db tty: true stdin_open: true db: image: mysql:5.7 volumes: - db-volume:/var/lib/mysql environment: MYSQL_ROOT_PASSWORD: password volumes: db-volume:rails ではデータベースも必要になるため、WebサーバだけではなくMySQLを用いたデータベースサーバのコンテナも構築します。
ここでは詳解しませんが、web という欄の中の「depends_on」で rails が連携するデータベースサーバ(コンテナ)を名前で指定しています。rails の立ち上げ
rails プロジェクトの作成 (rails new)
docker-compose.yml が置いてあるフォルダ(rails-dockerフォルダ)で以下の docker-compose コマンドを実行します。
$ docker-compose run web rails new . --force --database=mysqlコマンドについて、オプション含め少し読み方を解説します↓
- docker-compose: docker-compose というツールをつかって
- run: 以下のコマンドを実行します
- web: web コンテナで
- rails new: rails で新しいプロジェクトを作成する
- . : 現在のディレクトリに対して
- --force: 既存の file (ここでは Gemfile, Gemfile.lock) は上書きする形で
- --database=mysql: ただし rails のデータベースには MySQL を使用します
という意味を持っているのです。
この一行のコマンドを実行すると数分単位で処理に時間がかかるので、チョコレートでも食べながら気長に待ちましょう。
build の実行
Gemfile に追記された gem のインストール、
および作成されたファイルをコンテナ内に取り込むため build を実行します。$ docker-compose buildデータベース設定ファイルの編集
build が完了したら、rails で使用するデータベースファイルの設定を編集します。
config ディレクトリ内の database.yml というファイルが対象です。config/database.ymldefault: &default adapter: mysql2 encoding: utf8 pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> username: root password: password # 追加 host: db # 変更password は docker-compose.yml で指定したパスワードと合わせる必要があります。
docker-compose.yml... environment: MYSQL_ROOT_PASSWORD: password # ここと合わせる ...また、host の欄は MySQL コンテナ名を設定しています。
コンテナの起動
コンテナを起動するため、次のコマンドを実行します。
$ docker-compose up -ddocker-compose up がコンテナをdocker-compose.yml に基づいて起動するコマンドであり、
オプションの「-d」によりバックグラウンドで起動させることができます。データベースの作成
いまはまだコンテナが起動しただけであり、データベースは作成されていないので
次のコマンドを実行してデータベースを作成します。$ docker-compose run web bundle exec rails db:createrails を実行している web コンテナで、rails db:create (=データベースの新規作成)を実行する処理となっています。
rails 開発用サーバの起動確認
これで無事に rails の開発用サーバが起動したことになります。
ブラウザのアドレスバーに localhost:3000 と入力し、起動を確認してみましょう。rails ではお馴染みの画面が表示されれば完了です。
コンテナの停止
開発用サーバを止めるため、コンテナを一括で停止するには以下のコマンドを実行します。
$ docker-compose downまた立ち上げたいときには docker-comopose up -d を実行しましょう。
最後に
これによって作られるファイルの一式は Github にあげています。
フォルダ構成が分からなくなったり、自分の環境でうまくいっているか比較して確かめたい方はご参照ください。
- 投稿日:2020-11-15T17:03:29+09:00
Ruby on Railsの開発環境をDockerで構築する方法(Rails 5.x)
この記事では、Dockerを用いてRuby on Rails(以下、rails)の 5.x 系における開発環境を構築する方法を紹介します。
※Rails 6.x 系で Docker 環境を作りたい場合は こちら をご覧ください。Docker で環境構築をするメリットとは?
通常、rails の初学者の方はマシンに直接インストールして開発環境を構築する人がほとんどだと思います。
しかし、環境の微妙な際(Rubyのバージョンや環境変数など)によってつまづいた場合、
そのトラブルシューティングには多くの時間を要してしまうかと思います。そこで、Dockerで仮想環境上にrailsの環境を構築してあげることで
どのマシン上の開発環境でも等しい動作を行うことができるため、環境の差異によるエラーを避けることができます。
環境をいじりすぎた時には最悪一度リセットを出来るというのもメリットですね。また、作成したアプリケーションを本番環境として外部へ公開しようとするときにも
開発環境と本番環境での違いを最小限に抑え、安定したアプリケーションとして稼働させることができます.
Dockerで仮想環境を作って公開するなんて難しそうと思うかもしれませんが、
最近ではAWSといったクラウドサービスでもDockerコンテナをそのまま実行できるECS等のサービスが整っているので
Dockerで作った環境を公開するのも楽になってきています。前提条件
マシンに Docker がインストールされていること
Docker をインストールしていない人は、公式サイトから Docker のインストーラをダウンロードしてインストールしておきましょう。
→Docker公式また、任意の作業フォルダを作成しておきます。
ここでは "rails-docker" というフォルダを作成し、その中で作業していくことにします。$ mkdir rails-docker $ cd rails-dockerDocker関連ファイルの準備
rails-docker 内に以下4つの空ファイルを準備しておきます。
VScode を利用しているなら新規ファイルの作成で作ったり、
Mac のターミナルから touch コマンドを使ってもよいです。. ├── Dockerfile # 新規作成 ├── Gemfile # 新規作成 ├── Gemfile.lock # 新規作成 └── docker-compose.yml # 新規作成Dockerファイルの解説
Dockerでは、Dockerfileというファイルに基づいてコンテナのビルドが行われ、
Docker Image(コンテナの雛形)が作成されることになります。まず Dockerfile の全文を記載します。
使用する Ruby のバージョンは 2.7.0 としています。FROM ruby:2.7.0 RUN apt-get update -qq && apt-get install -y build-essential nodejs RUN mkdir /app WORKDIR /app COPY Gemfile /app/Gemfile COPY Gemfile.lock /app/Gemfile.lock RUN bundle install COPY . /app箇条書きとはなりますが、それぞれの意味を解説します。
- FROM ruby:2.7.0
- 公開されている ruby インストール済みコンテナのうち、Rubyのバージョンが 2.7.0 であるものを引っ張ってきます。
- RUN apt-get update -qq && apt-get install -y build-essential nodejs
- Rails の実行に必要なパッケージをインストールしています
- RUN mkdir /app
- Rails のプロジェクトファイルを作成するアプリディレクトリを作成
- WORKDIR /app
- 作業用のディレクトリを指定
- COPY Gemfile /app/Gemfile
- 自分のマシン(PC)上にある Gemfile をコンテナの作業用ディレクトリに移動させ、コンテナから利用できるようにします
- Gemfile.lock も同様
- RUN bundle install
- Gemfileに記載されているgemを一括インストール
- COPY . /app
- Dockerファイルが置いてあるフォルダのファイルすべてをコンテナ内の app ディレクトリにコピー
- Rails の実行に必要なファイルをコンテナに含めるためにコピーしています
Gemfile の解説
Ruby では Gemfile というファイルで環境で使いたい gem を定義することができます。
gemとはRubyのライブラリのことをいいます。
gemはRubyGemsと呼ばれるRuby用のパッケージ管理システムで管理されており、RubyGemsが提供するgemコマンドを通じてインストール等ができます。source 'https://rubygems.org' gem 'rails', '5.2.1'また少し解説します。
- source 'https://rubygems.org'
- gem のダウンロード元を指定
- gem 'rails', '5.2.1'
- インストールする gem である rails というパッケージ名と、バージョンを指定
- 今回は rails のバージョンは 5.2.1 を指定しています
- どのバージョンを指定してもよいのですが rails 6 以降は webpacker, yarn が必要になるので注意してください
- ここでは簡単に検証するため rails 5 系を使用
Gemfile があるディレクトリで "bundle install" コマンドを実行すると、
Gemfile の定義に従って、定義した gem をインストールすることができます。先ほど解説した Docker ファイルでも、Gemfile をコンテナの作業用ディレクトリにコピーして
そのフォルダ内で bundle install を実行するように指定していましたね。Gemfile.lock の解説
実は Gemfile.lock は最初の時点では何も書き込む必要がありません。
これは直接編集するようなファイルではなく、
Gemfile に基づいて bundle install を実行した後に
インストールされた gem が Gemfile.lock に一覧として記述されるようになります。使い方としては、Gemfile.lock を参照すればインストールされている gem とバージョンが分かり、
Gemfile.lock から bundle install を実行することもできるので
完全に同じ環境をもう一度作りたいときや、多人数で開発をするときに Gemfile.lock が使われます。Gemfile だけでも良いのでは?と思った鋭い方に補足しますと、Gemfile では実はこういう書き方もできることが理由の一つになっています。
gem 'rails', '~> 5.2.1'この場合、bundle install でインストールされる rails のバージョンは 5.2.1 以上であるものの
実際にインストールできる(公開されている)バージョンはその時点によって変わってきます。しかし Gemfile.lock では Gemfile に従ってインストールしようとした時に実際にインストールされた gem 本体と具体的なバージョン、
付随して必要となるためにインストールされたパッケージまで記録されます。少し整理すると Gemfile は「アプリで必要な gem の一覧」であり、
一方の Gemfile.lock は「Gemfile に従った結果、実際にインストールした gem の情報」が記述されることになります。docker-compose.yml の解説
この yml ファイルは Docker Compose で使用されます。
Docker Composeは、複数のコンテナで構成されるアプリケーションについて
Dockerイメージのビルドや各コンテナの起動・停止といった管理を行うためのツールです。Docker Composeでは、Dockerビルドやコンテナ起動のオプションも含め、複数のコンテナのための定義を docker-compose.yml というファイルに記述し、
それを利用して Docker イメージのビルドやコンテナ起動をすることができます。開発環境のように、Rails をそれ単体で動かすならば Dockerfile だけでもよいのですが
Rails を動かすためのアプリケーションサーバに加え、
実際に公開するときにはインターネットからのアクセスを受け付けるための Web サーバや、
またデータを保存・処理するためのデータベースサーバも用意することがあります。docker-compose.ymlversion: '3' services: web: build: . command: bundle exec rails s -p 3000 -b '0.0.0.0' volumes: - .:/app ports: - 3000:3000 depends_on: - db tty: true stdin_open: true db: image: mysql:5.7 volumes: - db-volume:/var/lib/mysql environment: MYSQL_ROOT_PASSWORD: password volumes: db-volume:rails ではデータベースも必要になるため、WebサーバだけではなくMySQLを用いたデータベースサーバのコンテナも構築します。
ここでは詳解しませんが、web という欄の中の「depends_on」で rails が連携するデータベースサーバ(コンテナ)を名前で指定しています。rails の立ち上げ
rails プロジェクトの作成 (rails new)
docker-compose.yml が置いてあるフォルダ(rails-dockerフォルダ)で以下の docker-compose コマンドを実行します。
$ docker-compose run web rails new . --force --database=mysqlコマンドについて、オプション含め少し読み方を解説します↓
- docker-compose: docker-compose というツールをつかって
- run: 以下のコマンドを実行します
- web: web コンテナで
- rails new: rails で新しいプロジェクトを作成する
- . : 現在のディレクトリに対して
- --force: 既存の file (ここでは Gemfile, Gemfile.lock) は上書きする形で
- --database=mysql: ただし rails のデータベースには MySQL を使用します
という意味を持っているのです。
この一行のコマンドを実行すると数分単位で処理に時間がかかるので、チョコレートでも食べながら気長に待ちましょう。
build の実行
Gemfile に追記された gem のインストール、
および作成されたファイルをコンテナ内に取り込むため build を実行します。$ docker-compose buildデータベース設定ファイルの編集
build が完了したら、rails で使用するデータベースファイルの設定を編集します。
config ディレクトリ内の database.yml というファイルが対象です。config/database.ymldefault: &default adapter: mysql2 encoding: utf8 pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> username: root password: password # 追加 host: db # 変更password は docker-compose.yml で指定したパスワードと合わせる必要があります。
docker-compose.yml... environment: MYSQL_ROOT_PASSWORD: password # ここと合わせる ...また、host の欄は MySQL コンテナ名を設定しています。
コンテナの起動
コンテナを起動するため、次のコマンドを実行します。
$ docker-compose up -ddocker-compose up がコンテナをdocker-compose.yml に基づいて起動するコマンドであり、
オプションの「-d」によりバックグラウンドで起動させることができます。データベースの作成
いまはまだコンテナが起動しただけであり、データベースは作成されていないので
次のコマンドを実行してデータベースを作成します。$ docker-compose run web bundle exec rails db:createrails を実行している web コンテナで、rails db:create (=データベースの新規作成)を実行する処理となっています。
rails 開発用サーバの起動確認
これで無事に rails の開発用サーバが起動したことになります。
ブラウザのアドレスバーに localhost:3000 と入力し、起動を確認してみましょう。rails ではお馴染みの画面が表示されれば完了です。
コンテナの停止
開発用サーバを止めるため、コンテナを一括で停止するには以下のコマンドを実行します。
$ docker-compose downまた立ち上げたいときには docker-comopose up -d を実行しましょう。
最後に
これによって作られるファイルの一式は Github にあげています。
フォルダ構成が分からなくなったり、自分の環境でうまくいっているか比較して確かめたい方はご参照ください。
- 投稿日:2020-11-15T17:01:09+09:00
Amazon S3に画像を保存する(Local/Heroku)
はじめに
自分用の備忘録
手順(Local)
Gemインストール
Gemfilegem "aws-sdk-s3", require: false保存先を変更
config/environments/development.rbconfig.active_storage.service = :local #下記に変更 config.active_storage.service = :amazonstorage.ymlに追記
config/storage.ymlamazon: service: S3 access_key_id: <%= ENV['AWS_ACCESS_KEY_ID'] %> secret_access_key: <%= ENV['AWS_SECRET_ACCESS_KEY'] %> region: ap-northeast-1 bucket: バケット名環境変数を設定
ターミナル# Catalina以降 % vim ~/.zshrc [insert mode] export AWS_ACCESS_KEY_ID="Access key ID" export AWS_SECRET_ACCESS_KEY="Secret access key" [:wqで保存] # 反映させるコマンド % source ~/.zshrc手順(Heroku)
※Gemインストール済
保存先を変更
config/environments/production.rbconfig.active_storage.service = :local #下記に変更 config.active_storage.service = :amazon環境変数を設定
ターミナルheroku config:set AWS_ACCESS_KEY_ID="Access key ID" heroku config:set AWS_SECRET_ACCESS_KEY="Secret access key"確認コマンド
ターミナル% heroku configpushして反映
おわりに
バケットは使いまわせるのでいちいち作らなくてよろしい。
✔︎
- 投稿日:2020-11-15T16:47:32+09:00
バリデーションの実行タイミングを指定するオプション 「on:」
はじめに/エラー発生時の状況
Railsでカレンダーアプリ(旅のしおり)的なものを実装中です。
日付や詳細、メンバー(deviseのユーザー登録機能を利用)を指定し、
旅行ページを登録します。旅行ページ(trips)の参加ユーザー(users)は、
下図のようにtrip_usersと言う中間テーブルで、多対多のアソシエーションを組んで紐付けています。という具合に保存されます。
しかし、実装を進めていると…何かの拍子に
見たことのないエラーと共に、、旅行ページの登録が突然出来なくなりました。
※当方、エラー文を日本語化しています。
先ほどまでと同じようにメンバーを選択しているのに、「参加メンバーは不正な値です」とは何ぞや…
私は何をしてしまったのでしょうか…開発環境
Ruby 2.6.5
Rails 6.0.3.4
MySQL
Visual Studio Code
(GoogleChrome)原因の考察・検証
binding.pry
でcreate処理を確認しました。[1] pry(#<TripsController>)> trip_params => <ActionController::Parameters { (中略) "user_ids"=>["", "2", "3", "4", "5", "6", "1"]} permitted: true> [2] pry(#<TripsController>)> @trip = Trip.new(trip_params) (中略) [3] pry(#<TripsController>)> @trip.valid? => false [4] pry(#<TripsController>)> @trip.errors.full_messages => ["参加メンバーは不正な値です"]参加ユーザーはparamsでは
user_ids
にidが格納されて送られます。
これに対して、「不正な値」と言われているようです。
さっきまでは全然不正じゃなかったのに、何が不正になったのでしょうか…何かエラーの心あたりはないものかと、最近何をやったか、記憶をたどると…
「そういえば、パスワードの英数混合バリデーション書いてなかったな〜」
と、急に思い出して書いた記述を発見しました。
user.rbclass User < ApplicationRecord (中略) PASSWORD_REGEX = /\A(?=.*?[a-z])(?=.*?\d)[a-z\d]+\z/i.freeze # 英数混合の正規表現 validates :password, format: { with: PASSWORD_REGEX, message: 'は英字と数字を両方含んでください' } end試しにコメントアウトしてみると…戻りました!
こちらが原因だった模様…どうやら、ユーザー新規登録時のバスワードに設定した英数混合バリデーションが、アソシエーションしている他モデルの生成時にも発動してしまっていた?みたいです。
数字のみのデータであるuser_ids
に対しても、「英数混合にしてください!」と弾いていたと考えられます。
そんな珍事件もあるの…では、そのバリデーションをユーザー登録のみに適用できないものか…
調べた結果…こちらが使えそうです!
結果
:on
オプションでバリデーション実行のタイミングを指定することができます。
今回はユーザー登録時にのみパスワードのバリデーションを実行したいので、on: :create
を記述します。↓修正後のUserモデル
user.rbclass User < ApplicationRecord (中略) PASSWORD_REGEX = /\A(?=.*?[a-z])(?=.*?\d)[a-z\d]+\z/i.freeze # 英数混合の正規表現 validates :password, format: { with: PASSWORD_REGEX, message: 'は英字と数字を両方含んでください' }, on: :create endこれでパスワードのバリデーションを残しつつ、
旅行ページの登録機能も今まで通りに戻りました!終わりに/感想
原因となる記述を探すのに時間がかかりました。
エラー時に遡れるように、マメにコミットする重要性も学びました…初学者で拙い記事ですが、少しでもお役に立てると嬉しく思います。
最後まで読んでいただき、誠にありがとうございました。参考記事
【公式/Railsガイド】Active Record バリデーション
【公式】Railsドキュメント/validates
【Qiita】状況によってsave時に実行するバリデーションを切り替える
【Qiita】Rails |onオプションを使ってバリデーションのタイミングを指定
- 投稿日:2020-11-15T16:47:32+09:00
バリデーションの実行タイミングを指定するオプション 【on:】
はじめに/エラー発生時の状況
Railsでカレンダーアプリ(旅のしおり)的なものを実装中です。
日付や詳細、メンバー(deviseのユーザー登録機能を利用)を指定し、
旅行ページを登録します。旅行ページ(trips)の参加ユーザー(users)は、
下図のようにtrip_usersと言う中間テーブルで、多対多のアソシエーションを組んで紐付けています。という具合に保存されます。
しかし、実装を進めていると…何かの拍子に
見たことのないエラーと共に、、旅行ページの登録が突然出来なくなりました。
※当方、エラー文を日本語化しています。
先ほどまでと同じようにメンバーを選択しているのに、「参加メンバーは不正な値です」とは何ぞや…
私は何をしてしまったのでしょうか…開発環境
Ruby 2.6.5
Rails 6.0.3.4
MySQL
Visual Studio Code
(GoogleChrome)原因の考察・検証
binding.pry
でcreate処理を確認しました。[1] pry(#<TripsController>)> trip_params => <ActionController::Parameters { (中略) "user_ids"=>["", "2", "3", "4", "5", "6", "1"]} permitted: true> [2] pry(#<TripsController>)> @trip = Trip.new(trip_params) (中略) [3] pry(#<TripsController>)> @trip.valid? => false [4] pry(#<TripsController>)> @trip.errors.full_messages => ["参加メンバーは不正な値です"]参加ユーザーはparamsでは
user_ids
にidが格納されて送られます。
これに対して、「不正な値」と言われているようです。
さっきまでは全然不正じゃなかったのに、何が不正になったのでしょうか…何かエラーの心あたりはないものかと、最近何をやったか、記憶をたどると…
「そういえば、パスワードの英数混合バリデーション書いてなかったな〜」
と、急に思い出して書いた記述を発見しました。
user.rbclass User < ApplicationRecord (中略) PASSWORD_REGEX = /\A(?=.*?[a-z])(?=.*?\d)[a-z\d]+\z/i.freeze # 英数混合の正規表現 validates :password, format: { with: PASSWORD_REGEX, message: 'は英字と数字を両方含んでください' } end試しにコメントアウトしてみると…戻りました!
こちらが原因だった模様…どうやら、ユーザー新規登録時のバスワードに設定した英数混合バリデーションが、アソシエーションしている他モデルの生成時にも発動してしまっていた?みたいです。
数字のみのデータであるuser_ids
に対しても、「英数混合にしてください!」と弾いていたと考えられます。
そんな珍事件もあるの…では、そのバリデーションをユーザー登録のみに適用できないものか…
調べた結果…こちらが使えそうです!
結果
:on
オプションでバリデーション実行のタイミングを指定することができます。
今回はユーザー登録時にのみパスワードのバリデーションを実行したいので、on: :create
を記述します。↓修正後のUserモデル
user.rbclass User < ApplicationRecord (中略) PASSWORD_REGEX = /\A(?=.*?[a-z])(?=.*?\d)[a-z\d]+\z/i.freeze # 英数混合の正規表現 validates :password, format: { with: PASSWORD_REGEX, message: 'は英字と数字を両方含んでください' }, on: :create endこれでパスワードのバリデーションを残しつつ、
旅行ページの登録機能も今まで通りに戻りました!終わりに/感想
原因となる記述を探すのに時間がかかりました。
エラー時に遡れるように、マメにコミットする重要性も学びました…初学者で拙い記事ですが、少しでもお役に立てると嬉しく思います。
最後まで読んでいただき、誠にありがとうございました。参考記事
【公式/Railsガイド】Active Record バリデーション
【公式】Railsドキュメント/validates
【Qiita】状況によってsave時に実行するバリデーションを切り替える
【Qiita】Rails |onオプションを使ってバリデーションのタイミングを指定
- 投稿日:2020-11-15T16:19:19+09:00
Rails migration カラムの変更やら何やらをまとめるんやで
この記事について
初心者が初心者に対して記述した初心者のRails記事
migrationの記述をすぐ忘れてまうこいつらをまとめるんやで〜
①現在のマイグレーションのバージョンを確認
②migrationを指定したバージョンまで戻す
③直前に実行されたマイグレーションを1つ取り消す
④カラムの変更
⑤インデックスの追加
①現在のマイグレーションのバージョンを確認
rails db:migrate:status database: first_development Status Migration ID Migration Name -------------------------------------------------- up 20201115064445 Create members②migrationを指定したバージョンまで戻す。
ファイル名にある数字の羅列(年月日時)を指定してコマンドを実行。
rails db:migrate VERSION=20201115064445③直前に実行されたマイグレーションを1つ取り消す
「あ、さっきの書き忘れてるやつあるわ!!!」って気づいた時に使える
rails db:migrate:rollback④カラムの変更
#migrationファイルを作成するためのコマンド rails g migration rename_変更前のカラム名_column_to_テーブル名(複数形) #migrationファイルの中身 class Rename変更前のカラム名ColumnToテーブル名s < ActiveRecord::Migration[5.2] def change rename_column :テーブル名(複数形), :変更前のカラム名, :変更後のカラム名 end end④カラムの変更
#migrationファイルを作成するためのコマンド rails g migration rename_変更前のカラム名_column_to_テーブル名(複数形) #migrationファイルの中身 class Rename変更前のカラム名ColumnToテーブル名 < ActiveRecord::Migration[5.2] def change rename_column :テーブル名(複数形), :変更前のカラム名, :変更後のカラム名 end end⑤インデックスの追加
#migrationファイルを作成するためのコマンド rails generate migration add_index_テーブル名_カラム名 #migrationファイルの中身 class AddIndexToテーブル名 < ActiveRecord::Migration def change add_index :テーブル名, カラム名 end endちなみにのインデックスの説明をしておこう
特定のカラムからデータを取得する際に、検索を高速化させる便利屋さん。
例えば、あるユーザーをnameで検索したい!となった時、Usersテーブルのnameカラムにインデックスがなかったら、Userテーブルのnameカラムを上から順に1つずつ確認して、該当ユーザーのデータを取得しようとする。
もし、これが何万人のデータを1から確認していくと、流石のプログラミング様でも時間がかかってしまう。
そこでUsersテーブルのnameカラムにindexを張ることで、アルファベット順にnameを並べ替え検索しやすいようにしてくれる。
- 投稿日:2020-11-15T16:11:30+09:00
初心者がWebアプリを公開してから1か月間でやったことまとめ[個人開発]
初めに
僕はちょうど一か月前にWebアプリを公開しました。そこで公開してから1か月間やったこと、立ちはだかった問題などを振り返ってみようと思います。
作ったWebアプリ
URL
https://www.code-sell.net/プログラムコードを販売できるサービスです。公開した直後にqiitaでも宣伝させていただきました。
コードを販売できるサービス「Code-sell」をリリースした!(個人開発)やったこと
ここからが本題です。どのようなことを、いつ、どうして、やったかを振り返っていきます
1 動作確認
いつ...公開した直後
どうして...開発環境ではうごいても本番環境では動かないということがあるからまあこれは皆さん普通にやると思います。ただ筆者の場合これが甘すぎました。
アカウント登録と有効化
コード販売
購入機能このくらいしかしませんでした。おかげで細かいところでエラーの目撃が相次ぎました。
送金機能、検索機能、アカウント編集機能、ページが開けないなど...
こういうのはサービスの信頼にかかわるのでしっかりやることをお勧めします。焦らずに、すべてのページを開いてすべての機能を試したほうがいいです。しかもこれをしっかりしなかったせいで、後で紹介する致命的バグに3,4週間くらい気づけないことになります。2 初期データ投稿
いつ...動作確認が終わってから
どうして...初期データがないと使ってもらえないからこれはいろんなサイトで言われていますが、だれも投稿していないあやしいサイトを使おうと思いますか?使わないでしょう。
だから初期データを作るか、トップページでは投稿されてるかどうかわからなくしたほうがいいです。3 めっちゃ宣伝
qiita
dev.to
service safari
つくろぐ
startapp
eggineer
ロケットリリース
note
zenn
crieitくらいですかね。宣伝できる場所があれば徹底的にやりました。特にqiitaとservice safariとnoteが効果が大きかったと思います。
4 バグ・エラー修正
いつ...qiitaなどで宣伝した直後(本当は動作確認で気づくべき)
がばがば動作確認によって発生したバグ・エラーたちを修正します。
5 機能追加
いつ...バグ・エラー修正が一通り終わったころ
公開してから追加した機能は
技術メモ...廃止済み
運営からのおしらせ...最初から実装しとくべきでした。
フォロー...なくてもいいかも
pv機能...なくてもいいかもくらいですかね。技術メモは「このままだとオワコンになるかも!」とか言って深夜テンションで作ってしまった黒歴史です。qiitaには投稿できないようなしょっぼいメモを残すというものです。全く使われませんでした。しかもサービスの機能の統一性がなくなるのでSEOも弱くなるかもしれません。
皆さんがwebアプリを作るときは一つのサービスだけをやりましょう。6 公式ツイッターをつくる
いつ...機能追加してる合間
これは、もっとユーザーが増えてからでもいいかもしれません。宣伝効果もないし、アップデートはお知らせでできるし。
7 致命的バグに気づく
いつ...公開してから3,4週間たってから
どうのようなバグかというと送金先がみんな同じになるというものです。
幸い、お金の取引はまだされていなかったので大丈夫でしたが本当に取り返しのつかなくなるところでした。なぜここまで発見が遅れたか
基本的な機能過ぎて逆に見直してなかった
管理画面を見てもアカウント数がある程度がないとわかならい
testコードを書いていなかった
一つや二つのアカウントだけで動作させているとわかならい(お問い合わせが来なかった理由)
みなさんも動作確認するとき1つや2つのユーザーではなくseedで5~10くらいのテストユーザーを作ってやりましょう。
8 SEO対策や速度改善(現在)
今は機能を追加するというよりSEO対策や速度改善などを中心にしています。トップページのデザインを細かく変えたり、サイトマップを作りGoogle Search Consoleに送ったり、余計なcssを消したりしています。
まとめ
ここ一か月いろいろありました。急にアカウント数が増えたり、バグに気づき冷や汗をかいたりすごい良い体験をして一か月だったなと思います。よかったら開発したアプリ使ってみてください。
- 投稿日:2020-11-15T16:11:30+09:00
個人開発でWebアプリを公開してからやった8つのこと
初めに
僕はちょうど一か月前にWebアプリを公開しました。そこで公開してから1か月間やったこと、立ちはだかった問題などを振り返ってみようと思います。
作ったWebアプリ
URL
https://www.code-sell.net/プログラムコードを販売できるサービスです。公開した直後にqiitaでも宣伝させていただきました。
コードを販売できるサービス「Code-sell」をリリースした!(個人開発)やったこと
ここからが本題です。どのようなことを、いつ、どうして、やったかを振り返っていきます
1 動作確認
いつ...公開した直後
どうして...開発環境ではうごいても本番環境では動かないということがあるからまあこれは皆さん普通にやると思います。ただ筆者の場合これが甘すぎました。
アカウント登録と有効化
コード販売
購入機能このくらいしかしませんでした。おかげで細かいところでエラーの目撃が相次ぎました。
送金機能、検索機能、アカウント編集機能、ページが開けないなど...
こういうのはサービスの信頼にかかわるのでしっかりやることをお勧めします。焦らずに、すべてのページを開いてすべての機能を試したほうがいいです。しかもこれをしっかりしなかったせいで、後で紹介する致命的バグに3,4週間くらい気づけないことになります。2 初期データ投稿
いつ...動作確認が終わってから
どうして...初期データがないと使ってもらえないからこれはいろんなサイトで言われていますが、だれも投稿していないあやしいサイトを使おうと思いますか?使わないでしょう。
だから初期データを作るか、トップページでは投稿されてるかどうかわからなくしたほうがいいです。3 めっちゃ宣伝
qiita
dev.to
service safari
つくろぐ
startapp
eggineer
ロケットリリース
note
zenn
crieitくらいですかね。宣伝できる場所があれば徹底的にやりました。特にqiitaとservice safariとnoteが効果が大きかったと思います。
4 バグ・エラー修正
いつ...qiitaなどで宣伝した直後(本当は動作確認で気づくべき)
がばがば動作確認によって発生したバグ・エラーたちを修正します。
5 機能追加
いつ...バグ・エラー修正が一通り終わったころ
公開してから追加した機能は
技術メモ...廃止済み
運営からのおしらせ...最初から実装しとくべきでした。
フォロー...なくてもいいかも
pv機能...なくてもいいかもくらいですかね。技術メモは「このままだとオワコンになるかも!」とか言って深夜テンションで作ってしまった黒歴史です。qiitaには投稿できないようなしょっぼいメモを残すというものです。全く使われませんでした。しかもサービスの機能の統一性がなくなるのでSEOも弱くなるかもしれません。
皆さんがwebアプリを作るときは一つのサービスだけをやりましょう。6 公式ツイッターをつくる
いつ...機能追加してる合間
これは、もっとユーザーが増えてからでもいいかもしれません。宣伝効果もないし、アップデートはお知らせでできるし。
7 致命的バグに気づく
いつ...公開してから3,4週間たってから
どうのようなバグかというと送金先がみんな同じになるというものです。
幸い、お金の取引はまだされていなかったので大丈夫でしたが本当に取り返しのつかなくなるところでした。なぜここまで発見が遅れたか
基本的な機能過ぎて逆に見直してなかった
管理画面を見てもアカウント数がある程度がないとわかならい
testコードを書いていなかった
一つや二つのアカウントだけで動作させているとわかならい(お問い合わせが来なかった理由)
みなさんも動作確認するとき1つや2つのユーザーではなくseedで5~10くらいのテストユーザーを作ってやりましょう。
8 SEO対策や速度改善(現在)
今は機能を追加するというよりSEO対策や速度改善などを中心にしています。トップページのデザインを細かく変えたり、サイトマップを作りGoogle Search Consoleに送ったり、余計なcssを消したりしています。
まとめ
今回学んだこと
動作確認は慎重に、seedを利用しテストユーザーを作ってからやる
宣伝、完璧な状態になってからやる
変なノリでいらない機能は作らない。
ここ一か月いろいろありました。急にアカウント数が増えたり、バグに気づき冷や汗をかいたりすごい良い体験をして一か月だったなと思います。よかったら開発したアプリ使ってみてください。
- 投稿日:2020-11-15T16:11:30+09:00
[個人開発]Webアプリを公開してからやった8つのこと
初めに
僕はちょうど一か月前にWebアプリを公開しました。そこで公開してから1か月間やったこと、立ちはだかった問題などを振り返ってみようと思います。
作ったWebアプリ
URL
https://www.code-sell.net/プログラムコードを販売できるサービスです。公開した直後にqiitaでも宣伝させていただきました。
コードを販売できるサービス「Code-sell」をリリースした!(個人開発)やったこと
ここからが本題です。どのようなことを、いつ、どうして、やったかを振り返っていきます
1 動作確認
いつ...公開した直後
どうして...開発環境ではうごいても本番環境では動かないということがあるからまあこれは皆さん普通にやると思います。ただ筆者の場合これが甘すぎました。
アカウント登録と有効化
コード販売
購入機能このくらいしかしませんでした。おかげで細かいところでエラーの目撃が相次ぎました。
送金機能、検索機能、アカウント編集機能、ページが開けないなど...
こういうのはサービスの信頼にかかわるのでしっかりやることをお勧めします。焦らずに、すべてのページを開いてすべての機能を試したほうがいいです。しかもこれをしっかりしなかったせいで、後で紹介する致命的バグに3,4週間くらい気づけないことになります。2 初期データ投稿
いつ...動作確認が終わってから
どうして...初期データがないと使ってもらえないからこれはいろんなサイトで言われていますが、だれも投稿していないあやしいサイトを使おうと思いますか?使わないでしょう。
だから初期データを作るか、トップページでは投稿されてるかどうかわからなくしたほうがいいです。3 めっちゃ宣伝
qiita
dev.to
service safari
つくろぐ
startapp
eggineer
ロケットリリース
note
zenn
crieitくらいですかね。宣伝できる場所があれば徹底的にやりました。特にqiitaとservice safariとnoteが効果が大きかったと思います。
4 バグ・エラー修正
いつ...qiitaなどで宣伝した直後(本当は動作確認で気づくべき)
がばがば動作確認によって発生したバグ・エラーたちを修正します。
5 機能追加
いつ...バグ・エラー修正が一通り終わったころ
公開してから追加した機能は
技術メモ...廃止済み
運営からのおしらせ...最初から実装しとくべきでした。
フォロー...なくてもいいかも
pv機能...なくてもいいかもくらいですかね。技術メモは「このままだとオワコンになるかも!」とか言って深夜テンションで作ってしまった黒歴史です。qiitaには投稿できないようなしょっぼいメモを残すというものです。全く使われませんでした。しかもサービスの機能の統一性がなくなるのでSEOも弱くなるかもしれません。
皆さんがwebアプリを作るときは一つのサービスだけをやりましょう。6 公式ツイッターをつくる
いつ...機能追加してる合間
これは、もっとユーザーが増えてからでもいいかもしれません。宣伝効果もないし、アップデートはお知らせでできるし。
7 致命的バグに気づく
いつ...公開してから3,4週間たってから
どうのようなバグかというと送金先がみんな同じになるというものです。
幸い、お金の取引はまだされていなかったので大丈夫でしたが本当に取り返しのつかなくなるところでした。なぜここまで発見が遅れたか
基本的な機能過ぎて逆に見直してなかった
管理画面を見てもアカウント数がある程度がないとわかならい
testコードを書いていなかった
一つや二つのアカウントだけで動作させているとわかならい(お問い合わせが来なかった理由)
みなさんも動作確認するとき1つや2つのユーザーではなくseedで5~10くらいのテストユーザーを作ってやりましょう。
8 SEO対策や速度改善(現在)
今は機能を追加するというよりSEO対策や速度改善などを中心にしています。トップページのデザインを細かく変えたり、サイトマップを作りGoogle Search Consoleに送ったり、余計なcssを消したりしています。
まとめ
今回学んだこと
動作確認は慎重に、seedを利用しテストユーザーを作ってからやる
宣伝、完璧な状態になってからやる
変なノリでいらない機能は作らない。
ここ一か月いろいろありました。急にアカウント数が増えたり、バグに気づき冷や汗をかいたりすごい良い体験をして一か月だったなと思います。よかったら開発したアプリ使ってみてください。
- 投稿日:2020-11-15T14:59:07+09:00
【Rails】投稿機能に公開・非公開機能を追加した時の実装手順
はじめに
制作しているポートフォリオに記事の公開・非公開機能を追加したので、実装手順を紹介します。
記事を投稿したけど、一旦非公開にしたい時があると思うので、実装してみました。前提
- kaminariのgemの機能を使用し、非公開記事一覧取得してます
- deviseのgemの機能を使用し、他ユーザーを非公開記事からリダイレクトさせてます
バージョン情報
- Ruby 2.6.3
- Rails 6.0.2.1
実装した手順
postsテーブルにstatus用のカラムを追加
$ rails g migration Add_status_To_posts status:integurdb/migrate/20201111213454_add_status_to_posts.rbclass AddStatusToPosts < ActiveRecord::Migration[6.0] def change add_column :posts, :status, :integer, null: false, default: 0 end endモデルを定義
app/models/post.rbclass Post < ApplicationRecord #・・・省略 enum status: { public: 0, private: 1 }, _prefix: true #・・・省略 end上記のように
_prefix: true
を記述してない状態でブラウザを開いたら、下記のエラーが出た。
エラー文を確認すると、public
というメソッドが重複しているとのこと。
重複してエラーが出ていなければ_prefix: true
を記述しなくてもOK。log/development.logArgumentError (You tried to define an enum named "status" on the model "Post", but this will generate a class method "public", which is already defined by Active Record.): app/models/post.rb:19:in `<class:Post>' app/models/post.rb:1:in `<main>' app/controllers/posts_controller.rb:85:in `set_post' Started GET "/posts/3" for 127.0.0.1 at 2020-11-12 06:52:36 +0900 Cannot render console from 172.22.0.1! Allowed networks: 127.0.0.0/127.255.255.255, ::1 [1m[35m (1.2ms)[0m [1m[35mSET NAMES utf8mb4, @@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[0m Processing by PostsController#show as HTML Parameters: {"id"=>"3"} Completed 500 Internal Server Error in 27ms (ActiveRecord: 0.0ms | Allocations: 5566)投稿編集ページで投稿ステータスを選択できるようにする
app/views/posts/_form.html.erb<%= form_with(model: post, local: true) do |form| %> <!--・・・省略・・・--> <%= form.label(:public, for: nil, class:'post-status__label') do %> <%= form.radio_button :status, :public %> <%= I18n.t('activerecord.attributes.post.statuses.public') %> <% end %> <%= form.label(:private, for: nil, class:'post-status__label') do %> <%= form.radio_button :status, :private %> <%= I18n.t('activerecord.attributes.post.statuses.private') %> <% end %> <!--・・・省略・・・--> <% end %>公開・非公開を選択するUIは下記のようにしました。
この記事でははその実装手順はメイントピックではないので、割愛。
しかし、この実装に時間かかってしまった。選択した投稿ステータスを保存できるようにする
app/controllers/posts_controller.rbclass PostsController < ApplicationController # ・・・省略 def create @post = Post.new(post_params) @post.user_id = current_user.id respond_to do |format| if @post.save format.html { redirect_to @post, notice: '新規投稿を行いました。' } format.json { render :show, status: :created, location: @post } else format.html { render :new } format.json { render json: @post.errors, status: :unprocessable_entity } end end end # ・・・省略 private # ・・・省略 def post_params params.require(:post).permit( :title, :content, :image, :status, # <= 追加:statusカラム {:cat_ids => []} ) end # ・・・省略 end非公開記事一覧、詳細ページは、他のユーザーにアクセス時にはリダイレクトさせる
app/controllers/posts_controller.rbclass PostsController < ApplicationController before_action :set_post, only: [:show, :edit, :update, :destroy] # GET /posts/1 # GET /posts/1.json def show if @post.status_private? && @post.user != current_user respond_to do |format| format.html { redirect_to posts_path, notice: 'このページにはアクセスできません' } end end # ・・・省略 end # ・・・省略 private def set_post @post = Post.find(params[:id]) end # ・・・省略 end記事一覧の取得方法
# 公開記事 $ Post.status_public.order(created_at: :desc).page(params[:page]) # 非公開記事 $ Post.status_private.order(created_at: :desc).page(params[:page]) # ランキング(Likeのトップ3) $ Post.status_public.joins(:likes).group(:post_id).order('count(likes.post_id) desc').limit(3)所要時間
作業内容 所要時間 見積 0.75H 実装・検証:新規投稿 7H 実装・検証:ユーザー詳細ページ 2.25H 実装・検証:投稿詳細ページ(localhost/posts/:id) 1H 実装・検証:記事一覧・詳細ページ(Like Ranking) 0.5H 実装・検証:記事一覧(localhost/posts) 0.125H 実装・検証:TOP(localhost) 0.125H 合計 12H 投稿編集ページを作るのに7Hかかってしまったので反省だが、なんとか作れたので良かったかな
さいごに
記事の公開・非公開機能追加の際にこの記事が参考になれば幸いです。
参考
【Rails】enumチュートリアル
Rails5 から enum 使う時は_prefix(接頭辞)_suffix(接尾辞)を使おう
- 投稿日:2020-11-15T14:24:11+09:00
ローカルで Rails アプリを production モードで起動する(API サーバー編)
ローカル で Rails を production モードで起動するまでの手順を書きます。
今回は、Rails を API サーバーとして使うのに必要な手順のみです。
環境
- macOS Catalina 10.15.7
- Ruby 2.7.2
- Rails 6.0.3.4
- MySQL 5.7.32
手順
database.yml の設定
動作確認のため、DB 名以外は development モードで起動していた時と同じ設定にしてしまえば良い。rails new した時のままであれば、だいたいこんな感じになるはず。
database.ymldefault: &default adapter: mysql2 encoding: utf8mb4 pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> username: root password: host: localhost development: <<: *default database: my_app_development test: <<: *default database: my_app_test production: <<: *default database: my_app_productionDB セットアップ
% RAILS_ENV=production bundle exec rails db:setup Created database 'my_app_production'
db:setup
だけで db:create, db:migrate, db:seed してくれるはず。サーバーを production モードで起動する
$ bundle exec rails s -e productioncurl などでアクセス確認できれば OK。
動作確認したアプリケーションは sidekiq を入れていたので、
/sidekiq
にアクセスしたが、問題なく動いた。おわりに
API サーバーだけとはいえ、こんなに簡単に動いたっけ?
昔より楽になったのかな。
- 投稿日:2020-11-15T14:15:01+09:00
ArgumentError(wrong number of arguments (given 0, expected 1))のエラーメッセージ
はじめに
「rails s」をしたときにdestroyメソッドに関するエラーが出て、どこを直せば良いのかよくわからずに苦労したので、記録として書きます。
エラーメッセージ
persistence.rb:325:in `destroy': wrong number of arguments (given 0, expected 1) (ArgumentError)原因
モデルの中の記述の仕方に問題がありました。
user.rbclass User < ApplicationRecord devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable attachment :profile_image has_many :tasks, dependent: destroy end解決策
すごく単純ですが、モデルの中のファイルの記述を治してあげれば解決しました。
user.rbclass User < ApplicationRecord devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable attachment :profile_image has_many :tasks, dependent: :destroy end
- 投稿日:2020-11-15T13:55:57+09:00
【Rails】Capistranoによる自動デプロイで発生したエラー(fatal: not a valid object name: master)
はじめに
現在プログラミングスクール卒業後、ポートフォリオ作成をしており、
Capistranoを使用した自動デプロイで発生したエラーを備忘録として投稿します。■開発環境
- Rails 5.0.7.2
- ruby 2.5.1
- AWS EC2
- Nginx
- Unicorn
- capistrano
Capistrano導入について
下記の記事を参考に、導入しました。
導入方法が分からない方は、私と同様に参考にしてみてください。
自動デプロイツール(Capistrano)導入方法発生したエラーについて
上記の記事を参考にCapistranoを導入し、自動デプロイを実行したところ、途中でエラーが発生しました。
ターミナル(ローカル環境)# アプリケーションのディレクトリで、下記の自動デプロイコマンドを実行する。 $ bundle exec cap production deploy自動デプロイコマンド実行後の、エラー内容はこちら。
ターミナル(ローカル環境)$ bundle exec cap production deploy [Deprecation Notice] Future versions of Capistrano will not load the Git SCM plugin by default. To silence this deprecation warning, add the following to your Capfile after `require "capistrano/deploy"`: require "capistrano/scm/git" install_plugin Capistrano::SCM::Git 00:00 git:wrapper 01 mkdir -p /tmp ✔ 01 ec2-user@52.193.230.41 0.239s Uploading /tmp/git-ssh-smot-production-nakayakouyuu.sh 100.0% 02 chmod 700 /tmp/git-ssh-smot-production-nakayakouyuu.sh ✔ 02 ec2-user@52.193.230.41 0.291s 00:00 git:check 01 git ls-remote git@github.com:nakaya-kousuke/smot.git HEAD 01 5e943870f1583d9775b045f3c40d418324d8ad8a HEAD ✔ 01 ec2-user@52.193.230.41 2.098s 00:02 deploy:check:directories 01 mkdir -p /var/www/smot/shared /var/www/smot/releases ✔ 01 ec2-user@52.193.230.41 0.132s 00:03 deploy:check:linked_dirs 01 mkdir -p /var/www/smot/shared/log /var/www/smot/shared/tmp/pids /var/www/smot/shared/tmp/cache /var/www/smot/shared/tmp/sockets /var/www/smot/sha… ✔ 01 ec2-user@52.193.230.41 0.224s 00:03 git:clone The repository mirror is at /var/www/smot/repo 00:03 git:update 01 git remote set-url origin git@github.com:nakaya-kousuke/smot.git ✔ 01 ec2-user@52.193.230.41 0.237s 02 git remote update --prune 02 Fetching origin ✔ 02 ec2-user@52.193.230.41 2.093s 00:06 git:create_release 01 mkdir -p /var/www/smot/releases/20201103133334 ✔ 01 ec2-user@52.193.230.41 0.226s 02 git archive master | /usr/bin/env tar -x -f - -C /var/www/smot/releases/20201103133334 02 fatal: not a valid object name: master 02 tar: 02 これは tar アーカイブではないようです 02 02 tar: 02 前のエラーにより失敗ステータスで終了します 02 #<Thread:0x00007f9e5507c1f0@/Users/nakaya-kousuke/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/sshkit-1.21.0/lib/sshkit/runners/parallel.rb:10 run> terminated with exception (report_on_exception is true): Traceback (most recent call last): 1: from /Users/nakaya-kousuke/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/sshkit-1.21.0/lib/sshkit/runners/parallel.rb:11:in `block (2 levels) in execute' /Users/nakaya-kousuke/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/sshkit-1.21.0/lib/sshkit/runners/parallel.rb:15:in `rescue in block (2 levels) in execute': Exception while executing as ec2-user@52.193.230.41: git exit status: 2 (SSHKit::Runner::ExecuteError) git stdout: Nothing written git stderr: fatal: not a valid object name: master tar: これは tar アーカイブではないようです tar: 前のエラーにより失敗ステータスで終了します (Backtrace restricted to imported tasks) cap aborted! SSHKit::Runner::ExecuteError: Exception while executing as ec2-user@52.193.230.41: git exit status: 2 git stdout: Nothing written git stderr: fatal: not a valid object name: master tar: これは tar アーカイブではないようです tar: 前のエラーにより失敗ステータスで終了します Caused by: SSHKit::Command::Failed: git exit status: 2 git stdout: Nothing written git stderr: fatal: not a valid object name: master tar: これは tar アーカイブではないようです tar: 前のエラーにより失敗ステータスで終了します Tasks: TOP => git:create_release (See full trace by running task with --trace) The deploy has failed with an error: Exception while executing as ec2-user@52.193.230.41: git exit status: 2 git stdout: Nothing written git stderr: fatal: not a valid object name: master tar: これは tar アーカイブではないようです tar: 前のエラーにより失敗ステータスで終了します上記のエラー内容を確認すると、git:create_releaseの部分でエラーが発生していることが分かります。
ターミナル(ローカル環境)※エラー文のとろこを抜粋00:06 git:create_release 01 mkdir -p /var/www/smot/releases/20201103133334 ✔ 01 ec2-user@52.193.230.41 0.226s 02 git archive master | /usr/bin/env tar -x -f - -C /var/www/smot/releases/20201103133334 02 fatal: not a valid object name: master 02 tar: 02 これは tar アーカイブではないようです 02 02 tar: 02 前のエラーにより失敗ステータスで終了します 02fatal: not a valid object name: master
こちらの内容が、今回のエラーの原因のようですので、
「masterが有効なオブジェクト名ではありません」と言われています。fatal: not a valid object name: master の解決方法を調べてみる!
fatal: not a valid object name: masterを検索して調べると、下記のような記事がたくさん出てきました。
Git エラー「fatal: Not a valid object name: 'master'.」の対処法
【Git】fatal: Not a valid object name: 'master'.の解決方法
fatal: Not a valid object name: 'master'. て言われたときどうした?
【Git】fatal: Not a valid object name: 'master'って怒られた【大体そんなもん】
こちらの記事を読んでみると、fatal: not a valid object name: masterはGitで発生しているエラーのようです。
ほとんどの記事に「マスターブランチにコミット」するように言われているので、下記のように実行。
ターミナル(ローカル環境)# コミットしたいファイルを全て選択する $ git add . # masterブランチへコミット $ git commit -m "fatal: not a valid object name: masterエラー解消のため" # 自動デプロイコマンドを実行 $ bundle exec cap production deployしかし、これでもfatal: not a valid object name: masterエラーは解決できませんでした!
ほかに何が原因か仮説を立ててみる!
Capistranoで、fatal: not a valid object name: master のエラーが出ている記事がひとつも出てこないので、仮説を立ててみました。
「masterが有効なオブジェクト名ではありません」と言われていて、Gitのエラーであることは分かりました。
そもそもGitを使っていて、masterブランチにもpush、commit、pullもできているのになぜ??エラー文の中にあるこれは tar アーカイブではないようですを調べてみると、下記の記事を見つけました。
capistranoエラーtar:これはtarアーカイブのようには見えません
こちらの記事のベストアンサーに、「gitから存在しないブランチを引っ張っている」と書かれていて、もしかしてmasterブランチがgitに存在していないからエラーが発生しているのか?と仮説を立てて調べてみました。
エラー解決!!
さっそく開発中のGitHubを調べてみると、デフォルトブランチが「master」ではなく、「main」になっていました!!!
そのため、Capistranoのgitのブランチを「main」に変更。
config/deploy.rb# config valid only for current version of Capistrano # capistranoのバージョンを記載。固定のバージョンを利用し続け、バージョン変更によるトラブルを防止する lock '3.14.1' # Capistranoのログの表示に利用する set :application, 'smot' # どのリポジトリからアプリをpullするかを指定する set :repo_url, 'git@github.com:nakaya-kousuke/smot.git' ---------- 追記 ---------- # ブランチを指定する set :branch, "main" -------------------------- # バージョンが変わっても共通で参照するディレクトリを指定 set :linked_dirs, fetch(:linked_dirs, []).push('log', 'tmp/pids', 'tmp/cache', 'tmp/sockets', 'vendor/bundle', 'public/system', 'public/uploads') set :rbenv_type, :user set :rbenv_ruby, '2.5.1' #カリキュラム通りに進めた場合、2.5.1か2.3.1です # どの公開鍵を利用してデプロイするか set :ssh_options, auth_methods: ['publickey'], keys: ['~/.ssh/smot.pem'] # プロセス番号を記載したファイルの場所 set :unicorn_pid, -> { "#{shared_path}/tmp/pids/unicorn.pid" } # Unicornの設定ファイルの場所 set :unicorn_config_path, -> { "#{current_path}/config/unicorn.rb" } set :keep_releases, 5 # secrets.yml用のシンボリックリンクを追加 set :linked_files, %w{ config/secrets.yml } # 元々記述されていた after 「'deploy:publishing', 'deploy:restart'」以下を削除して、次のように書き換え after 'deploy:publishing', 'deploy:restart' namespace :deploy do task :restart do invoke 'unicorn:restart' end desc 'upload secrets.yml' task :upload do on roles(:app) do |host| if test "[ ! -d #{shared_path}/config ]" execute "mkdir -p #{shared_path}/config" end upload!('config/secrets.yml', "#{shared_path}/config/secrets.yml") end end before :starting, 'deploy:upload' after :finishing, 'deploy:cleanup' endターミナル(ローカル環境)# 自動デプロイコマンドを実行 $ bundle exec cap production deployこれで自動デプロイに成功することができました!!!
補足情報
なぜGitHubのデフォルトブランチが「main」になっていたのか???を調べてみました。
■参考記事
GitHub、これから作成するリポジトリのデフォルトブランチ名が「main」に。「master」から「main」へ変更なんとGitHubのデフォルトブランチが「master」から「main」へ変更になっていました!
この変更には、2020年5月25日に米国ミネソタ州ミネアポリスでの事件をきっかけとした人権運動を背景にしたものとなっています。
このような事件が、IT業界にも影響されることもあると勉強になりました。
ちなみにGitHubの設定によって新規に作成するリポジトリのデフォルトブランチ名は任意に変更可能のようです。
- 投稿日:2020-11-15T13:43:56+09:00
Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2)のエラー
エラー内容
railsのアプリでデータベースを作成しようとしたところ、
$ bundle exec rake db:create warning ../../../package.json: No license field Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2) Couldn't create 'hello_rails_development' database. Please check your configuration. rake aborted! Mysql2::Error::ConnectionError: Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2)のエラーが発生。
解決策
mysqlが起動できない(Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2))
https://qiita.com/carotene4035/items/e00076fe3990b9178cc0
の記事を参考にmysqlサーバーを再起動しました。$ sudo mysql.server restart Password: Sorry, try again. Password: Sorry, try again. Password: sudo: 3 incorrect password attemptsしかし、Passwordが必要と言われ、MySQLのパスワードはとっくに忘れていたので、こちらの記事を参考にMySQLのパスワードを無しにしました。(本当ならrailsアプリのdatabase.ymlにパスワードを記述するべきですが。。。)
Mac ローカル環境の MySQL 8.x のrootパスワードを忘れた時のリセット方法
https://qiita.com/miriwo/items/1880e9d2ebcfd3c0e60d$ mysql.server stop $ mysqld_safe --skip-grant-tables & $ mysql -u root $ mysql.server status $ kill [番号] $ ps aux| grep mysqld $ kill [番号] $ mysql.server restart $ bundle exec rake db:createで無事にデータベースを作成できました。
- 投稿日:2020-11-15T10:47:33+09:00
Rails Tutorial 拡張機能のメッセージ機能を作ってみた(その2):表示する画面を作成
Rails Tutorialの第14章にある、メッセージ機能を作る件の続きです。
前回まででモデルができました。表示する画面を作ります。
DMを表示するViewの仕様を設計
DMを表示する方法を作ります。
tutorialの13.2 「マイクロポストを表示する」を読みます。MicropostのようにUserの画面に合わせて表示するのではなく、独立したページで表示することにします。Twitterと同様です。
モックアップを作ります。送信者が複数いるので、送信者が表示されているモックアップとして、図 14.5を参考にします。
DM(3)
画像1 Thomas Hobbes Lorem ipsum
sent 1 day ago.
画像2 Sasha Smith Also poor,nasty,
sent 2 days ago.
画像3 John Calvin Excepteur sint
sent 3 days ago.Previous 1 2 3 next
図 DMページのモックアップ
DMを表示するViewを作成
コントローラとビューを作成するために、コントローラを生成します。
ubuntu:~/environment/sample_app (create-dm) $ rails generate controller Dmsビューを作ります。リスト13.22と13.24を参考にします。
app/views/dms/show.html.erb<% provide(:title, @user.name)%> <div class="row"> <div class="col-md-8"> <% if @user.send_dms.any? %> <h3>DMs (<%= @user.sent_dms.count %>)</h3> <ol class="dms"> <li id="dm-<%= dm.id %>"> <%= link_to gravatar_for(dm.sender, size: 50), dm.sender %> <span class="user"><%= link_to dm.sender.name, dm.sender%></span> <span class="content"><%= dm.content %></span> <span class="timestamp"> Sent <%= time_ago_in_words(dm.created_at) %> ago. </span> </li> </ol> <% end %> </div> </div>DMを表示するコントローラーを作成
新しいDMのページを表示するためのコントローラーを作ります。
tutorialの「12.1.1 PasswordResetsコントローラ」を読みます。config/routes.rbRails.application.routes.draw do root 'static_pages#home' get '/help', to: 'static_pages#help' get '/about', to: 'static_pages#about' get '/contact', to: 'static_pages#contact' get '/signup', to: 'users#new' post '/signup', to: 'users#create' get '/login', to: 'sessions#new' post '/login', to: 'sessions#create' delete '/logout', to: 'sessions#destroy' resources :users do member do get :following, :followers end end resources :account_activations, only: [:edit] resources :password_resets, only: [:new, :create, :edit, :update] resources :microposts, only: [:create, :destroy] resources :relationships, only: [:create, :destroy] resources :dms, only: [:new, :create, :index, :destroy] end
HTTPリクエスト URL Action 名前付きルート GET /dms/new new new_dm_path POST /dms create dms_path GET /dms index dms_path DELETE dms/ destroy dm_path RESTfulルーティング
tutorialの「10.3.1 ユーザーの一覧ページ」を読みます。
リスト「 10.40: ユーザー一覧ページへのリンクを更新する 」にリンクを追加しているところがありました。同様に追加します。views/layouts/_header.html.erb<ul class="dropdown-menu"> <li><%= link_to "Profile", current_user %></li> <li><%= link_to "Settings", edit_user_path(current_user) %></li> <li><%= link_to "DM", dms_path %></li>画面を表示してリンクがメニューに追加されたことを確かめます。
ログインしていなかったらRedirectするテストは後で作ることにします。
コントローラーはindexなのに、viewはshowなことに気が付きました。ファイルをリネームします。
show.html.erb -> index.html.erbapp/controllers/dms_controller.rbclass DmsController < ApplicationController def index end end画面を試しに表示
rails serverで画面で表示してみます。
エラーになりました。メッセージは
undefined method `name' for nil:NilClass
で、エラーが起きた場所は
<% provide(:title, @user.name)%>
です。@userがnilなのだと考えます。
@userにどこでログインしたユーザーを設定するのか、userのshow画面を参考に見て同様に変更します。app/controllers/dms_controller.rbclass DmsController < ApplicationController def index @user = current_user end end画面で表示してみます。またエラーになりました。
undefined local variable or method `dm' for #<#<Class:0x00005575f57bf2a8>:0x00005575f57deb58>エラーが起きた場所は
<li id="dm-<%= dm.id %>">です。
コントローラーで@dmsにデータを入れる必要があると考えます。
micropostをhome画面に表示するところを参考に見てみます。コントローラーで
@micropost = current_user.microposts.build @feed_items = current_user.feed.paginate(page: params[:page])と@feed_itemsにデータを入れています。
ビューでは
<ol class="microposts"> <%= render @feed_items%> </ol>と@feed_itemsをrenderで一覧表示しています。参考にして変更します。
app/controllers/dms_controller.rbdef index @user = current_user @dms = @user.sent_dms endMicropostではfeedとfeed_itemsをうまく使っている13章を参考にするとよさそうです。読み返すなかで
render @userが何を意味しているのかがあやふやだったので、さかのぼって読み返します。
コントローラーにapp/controllers/dms_controller.rbdef index @user = current_user @dms = @user.sent_dms.paginate(page: params[:page]) endとすればよいと分かり、その場合viewの
<span class="user"><%= link_to dm.sender.name, dm.sender%></span>に何を書けばいいのか考えます。
app/views/dms/index.html.erb<% provide(:title, @user.name) %> <h1>DM</h1> <% if @user.sent_dms.any? %> <h3>DMs (<%= @user.sent_dms.count %>)</h3> <ol class= "microposts"> <%= render @dms %> </ol> <%= will_paginate @dms %> <% end %>app/views/dms/_dm.html.erb<li id="dm-<%= dm.id %>"> <%= link_to gravatar_for(dm.sender, size: 50), dm.sender %> <span class="user"><%= link_to dm.sender.name, dm.sender%></span> <span class="content"><%= dm.content %></span> <span class="timestamp"> Sent <%= time_ago_in_words(dm.created_at) %> ago. </span> </li>試しにrails serverで画面を表示してみます。データが少ないため1ページしかありません。
pagnateがされているか確認するために、データを増やします。
contentのテストデータ生成に、Faker::Hipster.sentenceを使ってみます。db/seeds.rb# DM users = User.order(:created_at).take(6) receiver = users.second 50.times do content = Faker::Hipster.sentence users.each {|user| user.sent_dms.create!(content: content, receiver_id: receiver.id) } end画面を表示してみます。
receiverが出ていないことに気が付きましたので、senderから変更します。
app/views/dms/_dm.html.erb<li id="dm-<%= dm.id %>"> <%= link_to gravatar_for(dm.receiver, size: 50), dm.receiver %> <span class="user"><%= link_to dm.receiver.name, dm.receiver%></span>DM表示のテスト作成
DMを表示する画面のテストを作ります。
tutorialの「13.2.3 プロフィール画面のマイクロポストをテストする」を参考にします。test/fixtures/dms.yml... <% 30.times do |n| %> dm_<%= n %>: content: <%= Faker::Hipster.sentence %> created_at* <%= 42.days.ago %> sender: michael receiver: archer <% end %>test/integration/dms_test.rbclass DmsTest < ActionDispatch::IntegrationTest def setup @user = users(:michael) end test "dm display" do log_in_as(@user) get dms_path assert_template 'dms/index' assert_select 'title', full_title(@user.name) assert_match @user.sent_dms.count.to_s, response.body assert_select 'div.pagination' @user.sent_dms.paginate(page: 1).each do |dm| assert_match CGI.escapeHTML(dm.content), response.body end endリスト 13.28を参考に、「'」などの記号が特殊文字で出力されていたので、エスケープする方法をネットで調べて修正しました。
https://rakuda3desu.net/rakudas-rails-tutorial14-3/コントローラーのアクセス制御のテスト作成
controllerのテストを作ります。
tutorialの「13.3.1 マイクロポストのアクセス制御」を読みます。test/controllers/dms_controller_test.rbtest "should redirect index when not logged in" do get dms_path assert_redirected_to login_url endREDです。コントローラーにindexアクションに対するアクセス制限を追加します。
app/controllers/dms_controller.rbclass DmsController < ApplicationController before_action :logged_in_user, only: [:index]テストがGREENになりました。
所要時間
11/7から11/14までの7.0時間です。
- 投稿日:2020-11-15T09:42:32+09:00
Rails <%= 式 %> 中身がない時にエラーが起こらない理由
この記事について
初心者が初心者に対して記述した初心者のRails記事
ずっと何となく疑問に思っていたこと。
controllerでインスタンス変数に何かしらの値を入れたつもりだったが実は中身が空(nil)やって、その変数をviewに渡しても何のエラーも起こらないことが謎やった。
なぜエラーが起こらないのか?
理由は
<%= 式 %>
が出力されるときは、式に対してto_sメソッド
が呼び出されているから。つまり、結果の出力はいつも自動的に
<%= 式.to_s %>
となっている。このメソッドが使える理由はRubyのオブジェクトは全てto_sメソッド
を持ち合わせているからやねんな。中身が入っていないインスタンスに対して
to_sメソッド
を呼びだす(nil.to_s
)と、空文字を出力するようになっている。やからエラーが出さず、そしてそのまま何も表示されないんやな。#controller @name = nil #view <p><%= @name.to_s %>さん</p> #出力 #=> さんまとめ
学びたての時はエラーが出ないから気付き辛いけど、「裏側ではこうなってるんだよー」って分かるとどんな問題が起こってるか発見しやすくなりそう。
やから、このままちゃんと学習を続けていこうと改めて思えた。
- 投稿日:2020-11-15T04:18:06+09:00
Can't connect to local MySQL server through socket '/var/lib/mysql/mysql.sock' (2)の解決まで。
原因:
helokuのMysqlとデプロイ使用しているデータベースが紐付けられていなかったため。
確認したこと
heroku info実行後、データベースの環境変数を確認
CLEARDB_DATABASE_URL: mysql://xxxxxxxxxxxx CLEARDB_DATABASE_GREEN: mysql://xxxxxxxxxxxx LANG: en_US.UTF-8 RACK_ENV: production RAILS_ENV: production RAILS_LOG_TO_STDOUT: enabled RAILS_SERVE_STATIC_FILES: enabled SECRET_KEY_BASE:あるはずのDATABASE_URL:がないし、mysql2でデータベース作ったはずなのに反映されてない・・・。
結論:
これは、ターミナルで
heroku config:set DATABASE_URL=mysql2${heroku_cleardb:5}の入力がなく、mysql2のデータベースに環境変数を格納できていなかったため。
最後に:
環境変数を変更するため、herokuのHPのアプリのページへ遷移し、settingsをクリック。
画面中央のReveal Config Varsをクリック。Config Varsを編集して終了。
無事、
heroku run rake db:migrate出来ました!
- 投稿日:2020-11-15T02:31:57+09:00
Railsにて検索機能を追加する。
概要
アプリケーション内でデータを検索できる機能があると、ユーザーがデータを探す際便利である。検索機能はSNS等でもよくある機能なので、実際に検索機能を実装してみる。
userというテーブルがあるとした場合、userを検索する機能を実装する。1.ルーティングの設定
userを扱うUserModel、UsersControllerは作成されていることを前提とします。
/config/routes.rb# 省略 resources :users do get "search", on: :collection endsearchアクションはリソースの集合を表すためon: :collectionを追加します。
2.モデルにクラスメソッドsearchを追加
/app/model/user.rbclass User < ApplicationRecord class << self def search(query) rel = order("id") if query.present? rel = rel.where("カラム名 LIKE ?, "%#{query}%") end rel end end endclass << self〜endでクラスメソッドを定義できます。
ローカル変数relを定義し、検索ワードが空でなければSQLのLIKEを使用し、該当カラムから対象のレコードを絞り込みます。3.コントローラにsearchアクションを追加
app/controllers/users_controller.rb# 省略 def search @users = User.search(params[:q]) render "index" endUserモデルで定義したクラスメソッドsearchをここで使用します。
4.viewにフォームを記載
app/views/users/index.html.erb# 省略 <%= form_tag :search_users, method: :get, class: "search" do %> <%= text_field_tag "q", params[:q] %> <%= submit_tag "検索" %> <% end %>form_tagは引数にパスを指定したフォームを作成します。デフォルトのmethodがPOSTであるため、getメソッドを指定しています。
text_field_tagでフォームを作り、検索ワード"q"は検索後もフォーム内に残しておくために第2引数にparams[:q]を指定しています。まとめ
以上で、検索機能を実装できます。
SQLを扱う箇所もありましたので、これを機会に勉強していきたいです。
- 投稿日:2020-11-15T00:20:10+09:00
【rails 初心者向け】複数Deviseモデルのログイン後の遷移先指定
管理者(admin)、顧客側(customer/user等)などを作成した際に、それぞれのログイン後のリダイレクト先を指定したい
前提
・devise使用モデルとしてadminモデルとcustomerモデルがある(モデル名は置き換えて考えてください)
実装
application_controller.erbclass ApplicationController < ActionController::Base def after_sign_in_path_for(resource) case resource when Admin admin_top_path #pathは設定したい遷移先へのpathを指定してください when Customer root_path #ここもpathはご自由に変更してください end end end記述してみると簡単で、すぐ覚えられそうですね。
記述内容について、少し解説してみます!
・after_sign_in_path_for(resource)
の引数(resource)の情報で条件分岐します。
・resouseインスタンスの中身が、AdminなのかCustomerなのかでcase文で処理をわけています。
ここまで調べるとresourceってどこから来てて、なんでこんな命名なんだ?@userとかでもいいんじゃないか、、とか考えてしまったので調べました。(以下は興味ある方のみ見てみてください)おまけ
registrations/new.html.erb<h2>Sign up</h2> <%= form_for(resource, as: resource_name, url: new_customer_registration_path(resource_name)) do |f| %> #以下省略上記のコードはDevise使用時に自動生成してくれるViewページの1つですね。
少し調べてみました。↓・Deviseは、複数のModelに対する認証を同時に扱えるフレームワークなので、どのようなModelのインスタンスが来ても大丈夫なように定義されているらしいです。
Deviseの内部では、それをresourceという名前で参照できるように統一しているようですね。
同様に、どのModelを認証しようとしているかの情報は、resource_nameという名前で参照できるようです。
・urlに(resource_name)が入っていますが、new_customer_registration_path
だけでは、どのModelの登録か分からないため(resource_name)が必要になるようです。少し物足りない説明になってしまったかもしれませんが、、普段便利すぎて何も考えずに実装してしまっていた部分についてある程度深堀り出来るいい機会になりました。
プログラミング経験数か月の新米ですので、おかしな部分等はご指摘お願いします、、!