- 投稿日:2020-09-09T21:03:10+09:00
技術記事を書く時に意識していること
自分のために書く
「思考の整理 + 未来の自分のための資料作りが目的で、周りからの反応はおまけ」
というスタンスでいると雑な記事をバンバン書ける。自分は執筆時間が5分かかっていない記事の方が多いと思う。そして記事数に対してLGTM等は少ない。自分の言葉で書く
普段自分が使う言い回しをした方が、後から振り返った時に理解しやすいです。カッコつけない方が良い。
文章は、きれいさや構造の正しさより、テンポを意識。Qiigleの検索に引っかかるようにする
将来検索に使いそうなワードは入れておく
https://qiigle.com/さっさと本題に入る
「記事の対象者」「この記事でわかること」「この記事で触れないこと」とかが必要なのは超長文記事か、反感を買いかねない踏み込んだ記事を書くときだけで良い。
あいさつとかギャグ的なものは技術記事には必要ない。その命名じゃないといけないのか、任意の名前で良いのかを明確にする
任意に名前をつけられる部分がアプリ名にちなんだ名前だったりすると、命名規則があるように見えてしまう。多少ダサくなってもわかりやすい方が良い。
参考記事を書く
モラル的な話だけじゃなくて、後から参照する時に執筆当時の状況を思い出しやすくなる。
記事投稿画面をブックマークバーにセット
1クリックで執筆開始できます。
伸ばしたい記事は拡散の努力をする
Qiitaではトレンドに入ると一気に伸びるので、サービス公開記事など伸ばしたい記事はTwitterやオンラインコミュニティ等でコメント付きで共有するなど、最大限拡散の努力をします。やりまくると嫌われるので、本当に伸ばしたい時だけ。
記事を伸ばすために学習するジャンルを変えない
経験上、Railsの記事を書くよりReact等の記事を書く方が全然伸びます。でもそれはそれなので、、
「記事が伸びる分野 = 自分が人柱になる確率が高い分野」で、今の自分は確実に技術力の土台を固めるフェーズなので、そこは履き違えないように注意してます。
- 投稿日:2020-09-09T19:17:26+09:00
yamlファイルのコメントアウトをエディタのショートカットで解除してエラーが出るときに確認すること
おことわり
Railsで遭遇したエラーではありますが、Rails以外でも起こりうるのでタイトルからRailsを外しています。
問題が起こった状況
Railsの設定ファイルの一つに、
storage.yml
という、ストレージ周りの設定をするyamlファイルがあるのですが、
その中に、デフォルトではコメントアウトされているAWSのS3に関する設定があります。今回、S3を設定する必要があったので、それを意気揚々とIDEのショートカットでコメントアウトを解除したときに問題が発生しました。
storage.ymltest: service: Disk root: <%= Rails.root.join("tmp/storage") %> local: service: Disk root: <%= Rails.root.join("storage") %> # Use rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key) # amazon: # service: S3 # access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %> # secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %> # region: us-east-1 # bucket: your_own_bucketどうなったか
下記エラーが発生しました
RuntimeError: YAML syntax error occurred while parsing /project_dir/config/storage.yml. Please note that YAML must be consistently indented using spaces. Tabs are not allowed. Error: (<unknown>): did not find expected key while parsing a block mapping at line 1 column 1どこが間違っていたか
storage.ymltest: service: Disk root: <%= Rails.root.join("tmp/storage") %> local: service: Disk root: <%= Rails.root.join("storage") %> # Use rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key) amazon: service: S3 access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %> secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %> region: us-east-1 bucket: your_own_bucketこの見た目ではわかりにくいですが、
amazon
というキーの行以降が半角スペース1つ分右にずれています。そのため、yamlの解釈エラーが出てしまっていたということになります。
対処法
ずれている分の半角スペースを削除する
結論
エディタのコメントイン機能を使う際はインデントに注意してください。
- 投稿日:2020-09-09T18:08:45+09:00
deviseを使ったユーザー情報の変更をする場合パスワードが保存されない時のedit画面での設定
deviseを使ったユーザー登録情報の編集をする場合、edit→updateを行うときビュー画面のエラーメッセージがCurrent password can't be blankと表示され、編集画面が保存されない場合の解決方法。
ruby '2.6.5'
rails '6.0.0
devise 4.7.22つのdeviseをもつアカウントがあり、ユーザー登録情報を編集し、保存をしたい場合の設定の流れをまとめます。
①ルーティングの確認/rails routes でuserのregistrations/editのパスを確認します。
edit_user_registration GET /users/edit(.:format) users/registrations#editedit_user_registration_path と確認が取れました。これをlink_toでパスを作成します。
②controllers/users/registrations_controller.rb ファイルです。
before_actionとconfigure_account_update_paramsの設定をします。今回は私のカラムを載せます。
before_action :configure_account_update_params, only: [:update]def configure_account_update_params devise_parameter_sanitizer.permit(:account_update, keys: [:nickname, :email, :password, :gender_id, :birth, :bloodtype_id, :emergencyperson, :emergencycall, :real_name, :real_name_kana, :phone_number]) end③次にビューです。
users/registrationsにedit.html.erbファイルを作成します。ほぼnew.html.erbをコピーしたものを使いますが、追加事項があります。私は2つのdeviseを使ってるのでディレクトリが違いますが1つのdeviseだとビューはdevise/registrationsにedit.html.erbを作成します。
form_withのパスも変更します。
参考サイト こちらです
結論から言うと、deviseのデフォルト設定なのですが、パスワード更新時は現在のパスワードを認証してupdateを行います。<%= form_with model: @user, url: user_registration_path, method: :patch, class: 'registration-main', local: true do |f| %><div class="field"> <%= f.label :current_password %> <%= f.password_field :current_password, autocomplete: "current-password" %> </div>こちらを追加します。編集する場合は今までのパスワードを入れる項目が必要と言うわけです。
これを追加することによって無事に変更項目が保存されました。ですがパスワードを変更しない場合も必須なのでそこが?と言う感じです。
引き続き調べて学習していきます。
- 投稿日:2020-09-09T16:32:30+09:00
【Rails】rakeタスクを実装する
rakeタスクとは
ファイルに記述した処理をコマンドラインから実行する機能です。
ユーザーの属性に応じてステータスを変更する、CSVデータをインポートする、任意のタイミングでユーザーにメールを送る、などなど様々な用途で使われます。基本的な使い方
タスクファイルを生成
$ rails g task qiita_task
実行したい処理を記述
namespace :qiita_task do desc 'hello worldします' task :hw do puts 'Hello World' end end実行
$ rake qiita_task:hw
その他
タスクの中にDBに接続する処理が含まれる場合
DBに接続する場合、以下のように
environment
と記述するnamespace :qiita_task do desc '最近登録したユーザーにメールを送信' task send_email_to_recent_users: :environment do recent_users = User.where('updated_at <= ?', Time.zone.parse('2020/09/08 15:50:00')) recent_users.each do |ru| ru.send_email end end end本番環境で実行する
プロジェクトのルートディレクトリ(Gemfileとかがあるとこ)で、
RAILS_ENV=production
をつけて実行。$ rake qiita_task:hw RAILS_ENV=productionタスク一覧を表示
デフォルトで定義されているタスクと、自分が作成したタスクがずらっと表示されます。
$ rake -T参考
- 投稿日:2020-09-09T16:16:08+09:00
[解答]rails にカート機能を追加する-モデル部分
購買者購入履歴のためのモデルを作る
ターミナル$ rails g model UserProductモデルとマイグレーションファイルができたと思うので、マイグレーションファイルを編集する
db/migrate/20190831095111_create_tags.rbclass CreateUserProducts < ActiveRecord::Migration[5.2] def change create_table :user_products do |t| t.integer :user_id #購入者 t.integer :product_id #商品 t.integer :lot #買った個数 t.timestamps end end end編集し終えたら、データベースを更新するためにマイグレーションをかける
ターミナル$ rails g db:migrate購買者購入履歴の関係をmodelで定義する。
・あるユーザーは多くの購買者購入履歴(買った商品)を持っている
・ある商品は多くの購買者購入履歴(買ったユーザー)を持っているので
多対多の関係app/model/user.rbclass User < ApplicationRecord devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable, :confirmable has_many :products has_many :user_products endapp/model/product.rbclass Product < ApplicationRecord belongs_to :user has_many :user_products endapp/model/user_product.rbclass UserProduct < ApplicationRecord belongs_to :user belongs_to :product end
- 投稿日:2020-09-09T15:34:46+09:00
commit failed exit code 1のエラー解決方法
【概要】
1.結論
2.なぜ"commit failed exit code 1"になるのか
3.どのように解決するか
4.ここから学んだこと
1.結論
git cloneしたファイルを削除する。
2.なぜ"commit failed exit code 1"になるのか
以前に
ターミナル% git clone https://github.com/"githubのアカウント名"/"githubのリモートレポジトリファイル名”.git
行ったことが原因でした。
上記のようにgitcloneしたものがcommitしたいファイルに残っていたせいでした。
3.どのように解決するか
該当ファイルの中の場所に"clone_site"と作ってありました。そのファイルを削除して、再度新たにgithubに反映させたいファイルを指定してcommitするとエラーは解消しました。
4.ここから学んだこと
当時git cloneした際は一瞬だけ少し試したいことがあったため使用しました。なので、用済みであればそのファイルは消すか、commitしない自信がそのファイルは使用しない方向にするべきでした。そうしないと、今回のようにgit cloneしたことを忘れかけていたので土壺にはまり時間をうばわれかねません。
- 投稿日:2020-09-09T15:23:43+09:00
Docker上のRailsをherokuへデプロイする
herokuへデプロイする練習
Dcokerコンテナ上に、Railsアプリを組んでCircleciを使って自動デプロイまでを一気にやろうとしたら
迷宮入りしたので、原点回帰しようと思います。(第2段)自分の環境
Ruby : 2.6.6
rails : 6.0.3.2
git : 2.23.0
heroku-cli : 7.42.13 darwin-x64 node-v12.16.2
Docker : 19.03.12開発環境は、MySQLで
本番環境は、PostgreSQLというパターンで組んでみようと思います。手元のDocker上にRailsアプリを用意する
まずは、アプリを作るディレクトリを作成し、そこにtouchコマンドで必要な諸々を用意します。
terminal$ touch {Dockerfile,docker-compose.yml,Gemfile,Gemfile.lock,entrypoint.sh}DockerfileFROM ruby:2.6 RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \ && echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list \ && apt-get update -qq \ && apt-get install -y nodejs yarn \ && mkdir /heroku_app WORKDIR /heroku_app COPY Gemfile //Gemfile COPY Gemfile.lock /heroku_app/Gemfile.lock RUN bundle install COPY . /heroku_app COPY entrypoint.sh /usr/bin/ RUN chmod +x /usr/bin/entrypoint.sh ENTRYPOINT ["entrypoint.sh"] EXPOSE 3006 CMD ["rails", "server", "-b", "0.0.0.0"]docker-compose.ymlversion: '3' services: db: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: password ports: - '3306:3306' command: --default-authentication-plugin=mysql_native_password volumes: - mysql-data:/var/lib/mysql:cached web: build: . command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3006 -b '0.0.0.0'" volumes: - .:/heroku_app ports: - "3006:3000" depends_on: - db stdin_open: true tty: true command: bundle exec rails server -b 0.0.0.0 volumes: mysql-data: driver: localGemfilesource 'https://rubygems.org' rails ‘6.0.3’entrypoint.sh#!/bin/bash set -e # Remove a potentially pre-existing server.pid for Rails. rm -f /heroku_app/tmp/pids/server.pid # Then exec the container's main process (what's set as CMD in the Dockerfile). exec "$@"上記5点を用意できれば、下記コマンドを実行しRailsアプリを作成します。
terminal$docker-compose run web rails new . --force --no-deps --database=mysql併せて、テキトーな中身を作っておきます。
$ docker-compose run web rails g scaffold blog title:string body:text $ docker-compose run web rails db:migrate $ docker-compose up -dherokuへデプロイする準備
続いて、Railsアプリをherokuにデプロイする前に、本番環境用にpostgreSQLを用意します。
- config/database.ymlの設定
- Gemfile にpgを追加
- config/enviroments/deviropment.rbの設定config/database.ymlの設定
config/database.ymlproduction: <<: *default adapter: postgresql encoding: unicode pool: 5Gemfileの設定
本番環境用にgemファイルを用意します。
productionのグループにpgを追加します。
また、MySQLは開発環境用として扱う様にするために、group :development, :test do
の中へ移動させます。Gemfilegroup :production do gem 'pg', '~> 0.19.0' endconfig/enviroments/deviropment.rbの設定
Rails6特有ですが、DNS離バインディング攻撃からの保護が入っているらしく、
hostを入れてあげる必要があります。config/enviroments/deviropment.rbconfig.hosts << "radiant-springs-45017.herokuapp.com"下記記事を参考にさせていただきました。
https://qiita.com/kodai_0122/items/67c6d390f18698950440編集を終えたら、ビルドします。
terminaldocker-compose build $docker-compose run web rails db:create $docker-compose up -dあとは、コマンド打って、ルンルンとherokuへデプロイします。
$ docker-compose down #一度落としておかないとエラーになる可能性があるとのこと $ heroku login $ heroku create アプリ名もしくは空欄 $ heroku container:login $ heroku container:push web $ heroku adding:create heroku-postgresql:hobby-dev $ heroku container:release web $ heroku open終わり!
割愛しましたが、herokuへデプロイした際にエラーが発生しました。
その際は、ターミナル上で$ heroku logs --tail
等を打ち、エラーを調べて解決させました。
成功された記事を参考した場合でも、環境の違い等でエラーが発生しうるので、理解のために都度調べる癖をつけるのは大事だと思った次第です。
- 投稿日:2020-09-09T14:36:34+09:00
【Rails】アソシエーションの種類(1対多 / 多対多)
- 投稿日:2020-09-09T13:48:57+09:00
【Ruby】関数における例外処理
はじめに
この記事は数多ある例外処理についての補足的なものです。具体的には、記事で紹介されてないパターンについて検証した結果を残します。
環境
- Mac
- Ruby 2.6.3
関数の中での使用
関数の中では以下のように書けます。
def method puts "hoge" a = 1 / 0 rescue ZeroDivisionError puts $! endbegin, endを省いた形ですね。このような形の場合、
a = 1 / 0がエラーを発生させるであろうことは一目瞭然です。しかし、以下の場合であればどうでしょうか。
def method # 膨大な処理1 # エラーが発生しそうな処理 # 膨大な処理2 rescue StandardError puts $! endさて、このコードを見た人がすぐにエラーの発生しそうな処理を見抜けるかというと怪しいところです。その場合はあえてbegin, endを書いたらいいと思います。
def method # 膨大な処理1 begin # エラーが発生しそうな処理 rescue StandardError puts $! end # 膨大な処理2 endこうすればどこがエラー発生しそうか分かりますし、沢山スクロールしてrescueを見に行く必要もありません。
ちなみにrescueでreturnすると膨大な処理2は処理されないので、間違いなく処理してほしいならensureに入れましょう。def method # 膨大な処理1 begin # エラーが発生しそうな処理 rescue StandardError puts $! return ensure # 膨大な処理2(rescueにreturnがあっても処理される) end endもしエラーの発生しそうな処理が複数ある場合は、関数の最後にまとめて書くのがシンプルでいいと思います。毎回begin, rescue, end書くのは流石に可読性下がりますので。何事も適切に。
def method # いろいろエラーが発生しそうな処理 # その他処理 rescue ZeroDivisionError, ArgumentError puts $! rescue StandardError puts $! puts $@ endちなみにStandardErrorを先に書くと大体のエラー処理はこいつがやってしまいます。なので子クラスから書いていきましょう。
- 投稿日:2020-09-09T10:45:16+09:00
素のrailsアプリをherokuにデプロイ
Dcokerコンテナ上に、Railsアプリを組んでCircleciを使って自動デプロイまでを一気にやろうとしたら
迷宮入りしたので、原点回帰しようと思います。herokuへデプロイする練習
自分の環境
Ruby : 2.6.6
rails : 6.0.3.2
git : 2.23.0
heroku-cli : 7.42.13 darwin-x64 node-v12.16.2Railsアプリを用意する
terminal$ rails new heroku_app #今回はheroku_appというものを用意 $ cd heroku_app $ rails g scaffold blog title:string body:text $ rake db:migrate $ rails sRailsアプリをデプロイする準備
- config/routes.rbの設定
- Gemfileの設定
- config/datebase.ymlの設定
- config/enviroments/production.rbの設定
config/routes.rbの設定
config/routes.rbRails.application.routes.draw do resources :blogs root 'blogs#index' #トップページをblogsコントローラのindexアクションに設定 endGemfileの設定
今回はherokuの無料枠で使えるpostgreSQLを使います。
ちなみにMySQLは有料枠になります。
※pg:production:本番環境のherokuは、postgreSQLを使うよって意味になります。Gemfilegroup :development, :test do gem 'sqlite3' #do ~ end内部に追加。他のgemは残してください。 end group :production do gem 'pg' endさっき更新したGemfileの変更をbundle installする。
terminal$ bundle install --without production※ちなみに、このオプションは一度打ち込めば、アプリ内部の/bundle/configというところに書き込まれるため、
それ以降は--without producitonを打たなくても本番環境以外のgemをインストールしてくれるようになります。
その他のbundle install時のオプションは全てここに書き込まれます。このようになっていたらOK
.bundle/configBUNDLE_WITHOUT: "production"cinfig/database.ymlの設定
実際にデータベースと接続する設定をします。
config/database.ymlproduction: #半角スペース2個分の空白を開ける <<: *default adapter: postgresql encoding: unicode pool: 5adapter: postgresql - postgreSQLのデータベースに接続します。
encoding: unicode - unicodeという文字コードを使用します。
pool: 5 - DBに接続できる上限の数を決めます。今回は5。config/environments/production.rbの設定
本番環境での動的な画像の表示をONにする。
config/enviroments/production.rb#デフォルトでfalseとなっている以下の箇所をtrueに変更 config.assets.compile = trueherokuへデプロイの準備を行う
git経由でアップロードをします。
teminal$ git init #このフォルダをgitと紐付けます。(最初の一回だけ)変更したら毎回やるやつ
terminal$git add -A #保存するファイルの選択。-Aは全部 $git commit -m "first commit" #addしたファイルの保存の確定herokuへ登録する
terminal$heroku login $heroku create アプリ名 #自由にどーぞ、空欄でも可。ちなみにすでに誰かが作って存在する場合はエラーが出るので、別名をトライ。 $git push heroku master本番環境でのマイグレーション
terminal$heroku run rails db:migrate終わり!
ちなみにheroku runをつけるとheroku上でrailsコマンドを打つことができます。
参考にさせていただいた記事通りに行えば、素のrailsをherokuにデプロイすることは容易にできました。
元記事の説明が大変丁寧でわかりやすく、感謝しています。
参考記事:https://qiita.com/kazukimatsumoto/items/a0daa7281a3948701c39
- 投稿日:2020-09-09T09:26:01+09:00
Rails 6で認証認可入り掲示板APIを構築する #4 postのバリデーション、テスト実装
←Rails 6で認証認可入り掲示板APIを構築する #3 RSpec, FactoryBot導入しpostモデルを作る
RSpecでmodelのテストを書く
とりあえずcreate, readが動くのは前回確認できました。
ここからはこの手順でいきます
- postのmodelテストを書く
- validationを実装する
- postのcontrollerテストを書く
- controller, routesを書く
- seedを書く
この記事ではとりあえず1.と2.の実装をして、3.以降はまた次回以降の記事で進めていきます。
rubocopをあらかじめつぶしておく
ドキュメンテーション書きなさいエラーがmigrationファイルにも出てくるので除外します。
.rubocop.ymlはこのように除外設定もできますが、本来必要なものも面倒がって除外していくとそもそもコーディング規約を守る意味が崩れるので、チーム開発で追加する際はしっかり議論しましょう。
.rubocop.yml+ # ドキュメンテーション + Style/Documentation: + Exclude: + - "db/migrate/**/*" ...まずmodelテストを書く
テスト駆動開発(TDD)っぽく、まずはRedのテストです。
バリデーション未実装なのでテストを書いて動かしてもRedとなるものを作ります。一旦factory_bot使わずに普通のRailsチックに書いてみます。
spec/models/post_spec.rb# frozen_string_literal: true require "rails_helper" RSpec.describe Post, type: :model do describe "subject" do context "blankの時に" do it "invalidになる" do post = Post.new(subject: "", body: "fuga") expect(post).not_to be_valid end end end end
- describeはテスト対象を示します
- contextは条件を示します
- it(もしくはexample)はテスト対象を示します
- expect(post).not_to be_validは、postがbe_validと等しくないことをテストしています
なので上記コードは『subjectがblankの時にinvalidになる』ことをテストしています。
ですが今のところpostモデルのsubjectにバリデーションを入れていないのでpostは有効であり、「invalidになる」テストは失敗することになります。$ rspec spec/models/post_spec.rb ... Finished in 0.07805 seconds (files took 3.53 seconds to load) 1 example, 1 failure Failed examples: rspec ./spec/models/post_spec.rb:8 # Post subject blankの時に invalidになる ec2-user:~/environment/bbs (master) $ rspec本当にsubjectが空でも登録できるか試してみましょう。
$ rails c [1] pry(main)> Post.create!(subject: "", body: "hoge") (0.1ms) BEGIN Post Create (2.5ms) INSERT INTO "posts" ("subject", "body", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["subject", ""], ["body", "hoge"], ["created_at", "2020-09-06 01:07:52.628768"], ["updated_at", "2020-09-06 01:07:52.628768"]] (0.9ms) COMMIT => #<Post:0x0000000005760700 id: 2, subject: "", body: "hoge", created_at: Sun, 06 Sep 2020 01:07:52 UTC +00:00, updated_at: Sun, 06 Sep 2020 01:07:52 UTC +00:00>保存できてしまいましたね。
ちなみにdescribeやcontextを使わずに
spec/models/post_spec.rb# frozen_string_literal: true require "rails_helper" RSpec.describe Post, type: :model do it "subjectがblankの時にinvalidになる" do post = Post.new(subject: "", body: "fuga") expect(post).not_to be_valid end endというコードでもほぼ同じテストコードの挙動になります。なぜなら、itブロックでexpectを書けばテストができるためです。
しかし同カラムや同バリデーション条件等をグルーピングして記述する際に分かりづらくなるので、基本的にdescribeやcontextを入れ子にして記述するのがオススメです。modelにバリデーションを追加する
blankをエラーとするバリデーション
app/models/post.rbclass Post < ApplicationRecord + validates :subject, presence: true end
これで、subjectカラムに対してpresence(存在)に対してtrue, つまりblankでの登録ができなくなります。
試してみましょう。
$ rails c [1] pry(main)> Post.create!(subject: "", body: "hoge") ActiveRecord::RecordInvalid: Validation failed: Subject can't be blank from /home/ec2-user/.rvm/gems/ruby-2.7.1/gems/activerecord-6.0.3.2/lib/active_record/validations.rb:80:in `raise_validation_error'登録できないですね。
$ rspec ./spec/models/post_spec.rb ... Finished in 0.05053 seconds (files took 1.63 seconds to load) 1 example, 0 failuresテストも通過しました。
最大文字数のバリデーション
文字数が無限に登録できると困るので制限を加えます。
こちらも先にテストから。
30文字以内ならOK、31文字以上はNGというバリデーションを追加する予定でテストを書いてみます。spec/models/post_spec.rbexpect(post).not_to be_valid end end + context "maxlengthにより" do + context "30文字の場合に" do + it "validになる" do + post = Post.new(subject: "あ" * 30, body: "fuga") + expect(post).to be_valid + end + end + context "31文字の場合に" do + it "invalidになる" do + post = Post.new(subject: "あ" * 31, body: "fuga") + expect(post).not_to be_valid + end + end + end end end
テスト実行してみましょう。
$ rspec ./spec/models/post_spec.rb ... Finished in 0.03204 seconds (files took 1.42 seconds to load) 3 examples, 1 failure Failed examples: rspec ./spec/models/post_spec.rb:21 # Post subject maxlengthにより 31文字の場合に invalidになるまだvalidationを追加していないので、30文字はパスしますが31文字はコケますね。
modelにvalidationを追加します。app/models/post.rbclass Post < ApplicationRecord - validates :subject, presence: true + validates :subject, presence: true, length: { maximum: 30 } end$ rspec ./spec/models/post_spec.rb ... Finished in 0.02201 seconds (files took 1.4 seconds to load) 3 examples, 0 failuresテスト通りましたね。
これで31文字の際にエラーになります。rails c
で試してみると良いでしょう。FactoryBotに置き換える
例えば先程のテストコードにある
post = Post.new(subject: "あ" * 30, body: "fuga")ですが、毎回body指定するの面倒ですよね。
2カラムであればまだ大丈夫ですが、これが10カラムとか超えてくると無駄にコードが長くなります。
その際にfactoryBotを使います。factoryBotはspec/factories/下を参照します。
今回はmodelを作った時の初期値から特に変える必要はありませんが、一応中身を見ておきます。spec/factories/posts.rb# frozen_string_literal: true FactoryBot.define do factory :post do subject { "MyString" } body { "MyText" } end endpost_spec.rbファイルを編集します。
spec/models/post_spec.rbdescribe "subject" do context "blankの時に" do it "invalidになる" do - post = Post.new(subject: "", body: "fuga") + post = build(:post, subject: "") expect(post).not_to be_valid end end context "maxlengthにより" do context "30文字の場合に" do it "validになる" do - post = Post.new(subject: "あ" * 30, body: "fuga") + post = build(:post, subject: "あ" * 30) expect(post).to be_valid end end context "31文字の場合に" do it "invalidになる" do - post = Post.new(subject: "あ" * 31, body: "fuga") + post = build(:post, subject: "あ" * 31) expect(post).not_to be_valid end endbuildはfactoryBotを使った
.new
に相当するものです。データベースへの保存は行われません。
今回の場合subjectを指定していますがbodyは未指定なので、factoryBotのbodyは"MyText"
が入ります。また、変更のたびにテスト実行してOKになることを確認してください。
変数をletに置き換える
とりあえず以下のように変更してみてください。
spec/models/post_spec.rbRSpec.describe Post, type: :model do describe "subject" do context "blankの時に" do + let(:post) do + build(:post, subject: "") + end it "invalidになる" do - post = build(:post, subject: "") expect(post).not_to be_valid end end context "maxlengthにより" do context "30文字の場合に" do + let(:post) do + build(:post, subject: "あ" * 30) + end it "validになる" do - post = build(:post, subject: "あ" * 30) expect(post).to be_valid end end context "31文字の場合に" do + let(:post) do + build(:post, subject: "あ" * 31) + end it "invalidになる" do - post = build(:post, subject: "あ" * 31) expect(post).not_to be_valid end endletは同一describeやcontextのブロック内のスコープに限定される変数です。
Rubyは最後に評価された式が返り値となるので、let(:post) do build(:post, subject: "あ" * 31) endの場合は、build実行結果のpostが、
let(:post)
によってpostという変数になります。演習
bodyにも必須制限・100文字以内制限のテストとバリデーションを実装してみましょう。
body実装回答例
spec/models/post_spec.rb# frozen_string_literal: true require "rails_helper" RSpec.describe Post, type: :model do describe "subject" do context "blankの時に" do let(:post) do build(:post, subject: "") end it "invalidになる" do expect(post).not_to be_valid end end context "maxlengthにより" do context "30文字の場合に" do let(:post) do build(:post, subject: "あ" * 30) end it "validになる" do expect(post).to be_valid end end context "31文字の場合に" do let(:post) do build(:post, subject: "あ" * 31) end it "invalidになる" do expect(post).not_to be_valid end end end end describe "body" do context "blankの時に" do let(:post) do build(:post, body: "") end it "invalidになる" do expect(post).not_to be_valid end end context "maxlengthにより" do context "100文字の場合に" do let(:post) do build(:post, body: "あ" * 100) end it "validになる" do expect(post).to be_valid end end context "101文字の場合に" do let(:post) do build(:post, body: "あ" * 101) end it "invalidになる" do expect(post).not_to be_valid end end end end endこの時点でrspec実行するとコケる
app/models/post.rb# frozen_string_literal: true # # 投稿クラス # class Post < ApplicationRecord validates :subject, presence: true, length: { maximum: 30 } validates :body, presence: true, length: { maximum: 100 } endrubocopがコケるので除外設定。testはDRYやらコーディング規約やら遵守すると逆効果のこともあるので、あまり厳しくしないほうがいいです。
.rubocop.yml+ # ブロック長さ + Metrics/BlockLength: + Exclude: + - "spec/**/*"
この時点でrspec, rubocop実行すると通る
続き
→Rails 6で認証認可入り掲示板APIを構築する #5 controller, routes実装
【連載目次へ】
- 投稿日:2020-09-09T08:53:57+09:00
rails tutorial 第7章
はじめに
独学でrails tutorialを進めていく過程を投稿していきます。
進めていく上でわからなかった単語、詰まったエラーなどに触れています。
個人の学習のアウトプットなので間違いなどあればご指摘ください。
初めての投稿なので読みにくいところも多々あるかと思いますがご容赦ください。
第7章 ユーザー登録
7.1.2 Usersリソース
RESTアーキテクチャの習慣に従ってユーザー情報をWebアプリケーション上に表示する。
RESTってなんだっけ、、、
RESTとは、アプリケーションを構成するコンポーネント(ユーザーやマイクロポストなど)を「リソース」としてモデル化することを指します。これらのリソースは、リレーショナルデータベースの作成/取得/更新/削除(Create/Read/Update/Delete: CRUD)操作と、4つの基本的なHTTP requestメソッド(POST/GET/PATCH/DELETE)の両方に対応しています。
コラム2.2(rails tutorial 2章より引用)
つまりはusersモデルをリソースとし、データ(作成、表示、更新、削除)操作とrequestメソッド(POST/GET/PATCH/DELETE)の両方に対応させるとういうイメージでしょうか。
そうすることで
・コントーラーやアクションの決定を簡略化
(リソースとすれば自動で設定してくれる)
・動的なWebページの作成
(ユーザー情報をWebアプリケーション上に表示するなど)
を実現できるということですかね。usersモデルをリソースとする際はルーティングで
resources :usersと記入するようです。
7.2.2 フォームHTML
新規ユーザーの登録フォームを作りました。分かりにくかった所だけ補足します。
<%= form_with(model: @user, local: true) do |f| %> <%= f.label :name %> <%= f.text_field :name %> <%= f.label :email %> <%= f.email_field :email %> <%= f.label :password %> <%= f.password_field :password %> <%= f.label :password_confirmation, "Confirmation" %> <%= f.password_field :password_confirmation %> <%= f.submit "Create my account", class: "btn btn-primary" %> <% end %>form_withはデフォルトで“remote” XHR requestを送信しますが、ここではエラーメッセージをほぼ確実に表示するために通常の“local”フォームリクエストを送信したいのです( 7.3.3)。(rails tutorial 7章より引用)
どういうことだろう、、、
“remote” XHR requestって??調べました。
XHRはAjaxと呼ばれるようなものらしい。
そしてajaxとは非同期通信のことで、ざっくり言うとjs形式のリクエストによってページ遷移を行わずページの一部の表示を変更したりできるものらしい。参考
https://qiita.com/__tambo__/items/45211df065e0c037d032rails tutorialではエラーメッセージをほぼ確実に表示するためhtml形式のリクエスト、つまり通常のlocalフォームリクエストを送信したいようです。
なぜremoteリクエストだとエラーメッセージの表示が確実ではないのだろう、、、
まぁ今作成しているsample_appではlocalリクエストで十分というのはわかりますけれど。余談ですがrails 5以前のform_tagやform_forではデフォルトがリモートフォームではなくローカルフォームだったようです。
次にHTMLのフォームタグについても無知でしたので調べました。
参考記事①
http://www.htmq.com/html5/form.shtml
フォームタグについてのざっくりとした説明。参考記事②
https://developer.mozilla.org/ja/docs/Learn/Forms/How_to_structure_an_HTML_form
labelついてなども書かれています。
ブロック変数(f)はform_withの第一引数に渡したオブジェクト(今回で言えば@user)の属性に対するinputタグを生成するメソッドを呼び出すもの。
参考
https://rakuda3desu.net/rakudas-rails-tutorial7-2/
Railsは@userのクラスがUserであることを認識します。また、@userは新しいユーザーなので、 Railsはpostメソッドを使ってフォームを構築すべきだと判断します。(rails tutorial 7章より引用)]
この部分についてどういうことか調べました。
参考記事①
https://qiita.com/hmmrjn/items/24f3b8eade206ace17e2
関連するモデルがあり@userがDBに存在するときはupdateアクションに、ないときはcreateアクションに飛ぶということのようです。参考記事②
https://pikawaka.com/rails/form_with
こちらの記事もわかりやすかったです。コントローラーで作成したインスタンスがnewメソッドで新たに作成されて何も情報を持っていなければ自動的にcreateアクションへ、findメソッドなどで作成され、すでに情報を持っている場合はupdateアクションへ自動的に振り分けてくれます。(上の記事より引用)
大分よく理解出来た気がします、、、
7.3.1 正しいフォーム
paramsには複数のハッシュに対するハッシュ(hash-of-hashes: 入れ子になったハッシュ)が含まれます。デバッグ情報では、フォーム送信の結果が、送信された値に対応する属性とともにuserハッシュに保存されています。このハッシュのキーが、inputタグにあったname属性の値になります。例えば次のように
<input id="user_email" name="user[email]" type="email" />"user[email]"という値は、userハッシュの:emailキーの値と一致します。(rails tutorial 7章より引用)]
ここ大事なポイント!!
7.3.2 Strong Parameters
params.require(:user).permit(:name, :email, :password, :password_confirmation)requireメソッドって?
受け取るパラメータ群を指定permitメソッドって?
利用可能なパラメータ名を指定参考
https://techacademy.jp/magazine/220787.3.4 失敗時のテスト
演習1
エラー発生!!test/integration/users_signup_test.rb#リスト 7.25: エラーメッセージをテストするためのテンプレート require 'test_helper' class UsersSignupTest < ActionDispatch::IntegrationTest test "invalid signup information" do get signup_path assert_no_difference 'User.count' do post users_path, params: { user: { name: "", email: "user@invalid", password: "foo", password_confirmation: "bar" } } end assert_template 'users/new' assert_select 'div#<CSS id for error explanation>' assert_select 'div.<CSS class for field with error>' end . . . endリスト7.25を記述してtestを行ったら
ERROR["test_invalid_signup_information", #<Minitest::Reporters::Suite:0x000000000adddc40 @name="UsersSignupTest">, 0.3698949000099674] test_invalid_signup_information#UsersSignupTest (0.37s) Nokogiri::CSS::SyntaxError: Nokogiri::CSS::SyntaxError: unexpected '#' after '[#<Nokogiri::CSS::Node:0x000000000addfc70 @type=:ELEMENT_NAME, @value=["div"]>]' test/integration/users_signup_test.rb:13:in `block in <class:UsersSignupTest>'とエラーが、、
リスト7.25ので追記されているは
assert_select 'div#<CSS id for error explanation>' assert_select 'div.<CSS class for field with error>'以上の2文です。
<CSS id for error explanation>あ。。。
ここはヒントが書かれているだけであって自分でそのidを書かないといけないじゃん、、、
ということでassert_select 'div#error_explanation' assert_select 'div.field_with_errors'と修正しました。
7.4.1 登録フォームの完成
app/controllers/users_controller.rb#リスト 7.26: 保存とリダイレクトを行う、userのcreateアクション class UsersController < ApplicationController . . . def create @user = User.new(user_params) if @user.save redirect_to @user else render 'new' end end private def user_params params.require(:user).permit(:name, :email, :password, :password_confirmation) end endredirect_to @user
上記のコードについて何となく意味はわかるが詳しく書かれている記事があるため参考にしました。
参考
https://qiita.com/Kawanji01/items/96fff507ed2f75403ecbしかし、 チュートリアル5.3.2 によると、Railsの規約では、基本的にはリンクには相対パスを使うべきですが、リダイレクトのリンクでは絶対パスを利用すべきとのことです。(上の記事より引用)
あれそうだったっけ?と調べると
Railsチュートリアルでは一般的な規約に従い、基本的には_path書式を使い、リダイレクトの場合のみ_url書式を使うようにします。これはHTTPの標準としては、リダイレクトのときに完全なURLが要求されるためです。(rails tutorial 5章より引用)]
本当でした、、、
7.4.3 実際のユーザー登録
問題発生!!
データベースをリセットするため、rails db:migrate:resetを実行。
しかしリセットできない。ermission denied @ apply2files - C:/environment/sample_app/db/development.sqlite3 Couldn't drop database 'db/development.sqlite3' rails aborted! Errno::EACCES: Permission denied @ apply2files - C:/Users/81801/environment/sample_app/db/development.sqlite3 bin/rails:4:in `require' bin/rails:4:in `<main>' Tasks: TOP => db:drop:_unsafe (See full trace by running task with --trace)解決
以下の記事を参考に解決しました。
https://teratail.com/questions/67393
https://qiita.com/Toshiki23/items/f366504844fd22ad87d9どうやらwindowsでデータベースにsqlite3を利用していると
rails dropコマンド
rails db:resetコマンド
rails db:migrate:resetコマンド
において問題が発生してしまうようです。
悲しいかなwindows...記事にあるようdb/development.sqlite3ファイルをエディタのエクスプローラーより手動で消し(コマンドプロンプトではrmコマンドがフォローされていなかったため)、
rails db:create db:migrateを改めて実行したところ
Database 'db/development.sqlite3' already exists Database 'db/test.sqlite3' already existsあれ?消したはずなのに残っている、、、
使用しているエディタからは確かにdevelopment.sqlite3ファイルは消えている、、、、、、、どうしよう!!!!!
とりあえず削除したdevelopment.sqlite3ファイルの行方を捜してみました。参考
https://maitakeramen.hatenablog.com/entry/2018/02/08/131755ゴミ箱を探してもなぜか見当たらない、、、
(やっぱり削除されていないからか??)探しに探し、windowsのエクスプローラーからsample_app/dbディレクトリを見てみるとdevelopment.sqlite3ファイルがありました!!
とりあえず開いているエディタのdbディレクトリにコピーしました。
試しにDB Browser for SQliteでデータを覗いて見たらusersテーブルのデータは空になっていました。
原因を調べてみましたがわかりませんでした。
とりあえずデータベースのリセットは達成したこととし、次に進むことにします、、、余談
この後に、Git Bashを使えばLinuxコマンドも使えるということを教えてもらい、以降はそちらも使って学習を進めることとしました。7.4.4 成功時のテスト
演習2
演習は問題なくクリア出来ましたが、content_tagヘルパーについて調べました。
content_tagヘルパー
content_tag(:要素名, 表示させる内容, オプション)参考
https://techacademy.jp/magazine/42167終わりに
内容が急に難しくなってきました。
tutorialでは当たり前に使われていたUserリソースのような概念を理解するのも大変でした。
form_withヘルパーを使ったフォームの生成も一度ではなかなか理解できず、何度か読み返しました。
度々エラーにも遭遇し、時間がかかりました。
- 投稿日:2020-09-09T00:50:22+09:00
自分用 rails 定数の置き場所
置き場所
config/initializers/**.rbmodule Constants CONSTANT = '定数だよー' end Constants.freeze使い方
Constants::CONSTANT #=> '定数だよー'配列など場合によってfreezeを使い分ける