- 投稿日:2020-02-20T21:33:24+09:00
複数テーブルに存在するカラムのi18n定義を共通化する
複数テーブルに共通して存在するカラム(ex.
created_at
,updated_at
)を各テーブルで定義するのは冗長でめんどくさい。config/locales/translation_ja.ymlja: activerecord: attributes: user: name: ユーザ名 created_at: 登録日時 updated_at: 更新日時 event: name: イベント名 created_at: 登録日時 updated_at: 更新日時こんな風に書かなくても、
attributes
直下で定義すれば使い回すことができる。config/locales/translation_ja.ymlja: attributes: created_at: 登録日時 updated_at: 更新日時<%= l(User.created_at) %> => 登録日時
- 投稿日:2020-02-20T20:52:56+09:00
deviseにユーザー詳細や編集機能を付ける
はじめに
deviseを使ってログイン機能を作った後にユーザー情報のページを実装したいと思い作ってみたら変なところで詰まったので書いておくことにしました。(備忘録)
前提
gem 'devise'
でログイン機能実装済み(email,password)ユーザーの一覧ページを作る
$ rails g controller users indexroutes.rbdevise_for :users resources :usersresourcesをdivise_forの下に書く。上に書くとdeviceが優先されてログイン、ログアウトの時にidが変なところに入ってバグる。
indexページのルーティングとビューは各々好きに作る。
カラムの追加
$ add_name_to_users name:string $ rails db:migrateこの呪文をいつも忘れてググってるのでここに書いておくことにしました。名前以外にも色々追加する。
機能実装
詳細、編集、削除を実装して完成。
- 投稿日:2020-02-20T20:13:39+09:00
Railsチュートリアルの開発環境を Docker でもっと便利になイカ!?
【概要】
本記事では、Gem の永続化、データベース及びテスト用メールサーバをコンテナ化していきます。
Railsチュートリアルの開発環境を Docker にしてみなイカ?の続きです。さて、前回の記事では、Railsチュートリアルの Sample App をコンテナ化しました。
しかし、実際の開発にあたり、煩わしい部分や便利になるところがありますので、改善していきましょう。
- 開発環境
- macOS Mojave: 10.14.6
- Docker Engine: 19.03.5
- Docker Compose: 1.25.4
- コンテナ環境
- Alpine Linux: 3.11.3
- Ruby: 2.4.9
- Rails: 5.1.2
- 参考: 学習情報 URL
【本文】
□ 事前準備
- 前回の記事と同じファイル及びディレクトリ構成です。
docker/DockerfileFROM ruby:2.4.9-alpine3.11 ENV LANG C.UTF-8 \ TZ Asia/Tokyo ENV BUILD_PACKAGES="build-base" \ DB_PACKAGES="sqlite-dev postgresql-dev" \ RAILS_PACKAGES="tzdata nodejs imagemagick" \ FAVORITE_PACKAGES="less" RUN apk update && \ apk upgrade && \ apk --update --no-cache add \ ${BUILD_PACKAGES} \ ${DB_PACKAGES} \ ${RAILS_PACKAGES} \ ${FAVORITE_PACKAGES} WORKDIR /app COPY Gemfile \ Gemfile.lock \ /app/ RUN bundle install --jobs=4 # 下記のコードは、頻繁に Gemfile を変更する場合、コメントアウトを推奨します。 # RUN apk del ${BUILD_PACKAGES} # https://github.com/bundler/bundler/issues/6154 ENV BUNDLE_GEMFILE='/app/Gemfile'docker-compose.ymlversion: '3' services: app: build: context: . dockerfile: ./docker/Dockerfile ports: - 3000:3000 command: bundle exec rails s -p 3000 -b 0.0.0.0 volumes: - ./:/app:cached stdin_open: true tty: true□ Gem の永続化
■ 現状の問題点
- 結論から言うと、現状では Gemfile を編集する度に
docker-compose build
をする必要があリます。つまり、大量の Gem を初めから全てbundle install
することになる煩わしい状態なのです。- 理由としては、コンテナの構造によるものであり、この問題点を体験したい場合、事前準備の条件で以下の通り動かしてみると良いでしょう。1
bash(省略可)# (1) コンテナのイメージ構築(ここのイメージがコンテナ構築時のデフォルト設定) $ docker-compose build # (2) コンテナを構築及び起動 $ docker-compose up -d # (3) Gemfile に Gem を追加 ... group :development, :test do ... gem 'pry-rails' gem 'pry-byebug' end ... # (4) 起動済のコンテナ内で bundle install $ docker-compose exec app bundle install # (5) 追加した Gem が確認できる( /usr/local/bundle/ にインストールされている) $ docker-compose exec app gem list # (6) コンテナを削除 $ docker-compose down # (7) コンテナを起動すると... $ docker-compose up > Starting sample_app_app_1 ... done > Attaching to sample_app_app_1 > app_1 | Could not find pry-byebug-3.4.3 in any of the sources > app_1 | Run `bundle install` to install missing gems. > sample_app_app_1 exited with code 7 # (8) 結果 - pry-byebug が見つからないため、bundle install が必要な状態です。 - つまり、コンテナの状態が(1)に戻ってきていることがわかります。■ 解決策
- 前述の通り原因は、
起動したコンテナ内で bundle install
するだけで、設計図となるコンテナのイメージ自体に反映されていないことです。解決策としては、Gem をコンテナ外で管理することです。コンテナを削除しても、Gem が存在する場所に影響しません。ここでは、コンテナ外に Gem の保管場所( = volume )を作成して、コンテナ内の Gem を保存する場所と繋げます( = mount )。
参考URL
○ 1: docker-compose.yml に data volume を追加
docker-compose.ymlversion: '3' services: datastore: image: busybox + volumes: + - bundle_install:/usr/local/bundle app: build: context: . dockerfile: ./docker/Dockerfile ports: - 3000:3000 command: bundle exec rails s -p 3000 -b 0.0.0.0 volumes: + - bundle_install:/usr/local/bundle - ./:/app:cached stdin_open: true tty: true + volumes: + bundle_install:○ 2: 再度 bundle install
bash# 前の手順で、Gemfile に Gem を追加していましたら、該当箇所を一度コメントアウトしてください。 # (1) コンテナのイメージ構築 $ docker-compose build # (2) コンテナを構築及び起動 $ docker-compose up -d # (3) Gemfile に Gem を追加 ... group :development, :test do ... gem 'pry-rails' gem 'pry-byebug' end ... # (4) 起動済のコンテナ内で bundle install $ docker-compose exec app bundle install # (5) コンテナを削除 $ docker-compose down # (6) コンテナを起動すると正常に動作しています。 $ docker-compose up○ 3: どこに保存されたのか?
- では、データの保管先を確認してみましょう。
- 追加した volume は、通常のディレクトリに存在せず、別の場所に存在しています。なお、ディレクトリ内を探検してみると、コンテナも保存していることがわかります。
bash(省略可)# Moby VM にアクセス $ screen ~/Library/Containers/com.docker.docker/Data/vms/0/tty -s # 〜VM内操作へ移行〜 # 以下のコマンドにより、`sample_app_bundle_install/`の存在が確認できる。 $ ls /var/lib/docker/volumes/ # この Volume は、data_store で定義した通り、`/usr/local/bundle`を保存する。 $ ls /var/lib/docker/volumes/sample_app_bundle_install/_data # 終了時 $ `controll` + `A` + `K` $ yes○ 4: Gem の永続化完了
- ここまでで、Gem の永続化が完了したため、次はデータベースをコンテナ化します。
- もちろん、データベースをコンテナ化したとしても、コンテナの削除時にデータベースに保存したデータは失われてしまいます。そのため、Gem の永続化と同様にデータベースも併せて永続化していきます。
□ データベースのコンテナ化
■ 現状の問題点
- Sample App における本番環境のデータベースは PostgreSQL で、開発環境は SQLite を使用しています。しかし、基本的に開発環境と本番環境は、同一化が求められます。「開発環境では動いたのに本番で動かない(絶望)」ということ自体があってはなりません。
しかし、これを実践しようとしてローカル環境で開発するとき、逐一 PostgreSQL を起動したり、プロジェクトによって異なるバージョンを用意する必要があったり、煩わしい部分があるため、コンテナ化します。
参考URL
■ 解決策
○ 1: docker-compose.yml の編集
docker-compose.ymlversion: '3' services: datastore: image: busybox volumes: - bundle_install:/usr/local/bundle + - db_data:/var/lib/postgresql/data + db: + image: postgres:12.2-alpine + volumes: + - db_data:/var/lib/postgresql/data + ports: + - 5432:5432 + environment: + POSTGRES_USER: root + POSTGRES_PASSWORD: pass app: build: context: . dockerfile: ./docker/Dockerfile ports: - 3000:3000 command: bundle exec rails s -p 3000 -b 0.0.0.0 volumes: - bundle_install:/usr/local/bundle - ./:/app:cached + depends_on: + - db + environment: + APP_DATABASE_HOST: db + APP_DATABASE_USERNAME: root + APP_DATABASE_PASSWORD: pass stdin_open: true tty: true volumes: bundle_install: + db_data:○ 2: config/database.yml の編集
- Rails の DB 設定は、SQLite のままであるため、PostgreSQL に変更します。
- なお、ここでは差分表示していませんので、全て置換してください。
config/database.ymldefault: &default adapter: postgresql pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> host: <%= ENV.fetch("APP_DATABASE_HOST") { '127.0.0.1' } %> port: <%= ENV.fetch("APP_DATABASE_PORT") { '5432' } %> username: <%= ENV.fetch("APP_DATABASE_USERNAME") { 'root' } %> password: <%= ENV.fetch("APP_DATABASE_PASSWORD") { 'pass' } %> development: <<: *default database: sample_app_development test: <<: *default database: sample_app_test production: <<: *default database: sample_app_production○ 3: Gemfile の編集
- 開発環境を PostgreSQL に変更するため、現在の Gemfile の構成もついでに変更しておきます。
Gemfile... group :development, :test do - gem 'sqlite3', '1.3.13' gem 'byebug', '9.0.6', platform: :mri gem 'pry-rails' gem 'pry-byebug' end ... - group :production do - gem 'pg', '0.18.4' - end + gem 'pg', '0.18.4' ...○ 4: DB コンテナの起動
bash# コンテナと volume を削除してから起動 $ docker-compose down -v $ docker-compose up -d○ 5: データベースの作成
bash$ docker-compose exec app rails db:create $ docker-compose exec app rails db:migrate $ docker-compose exec app rails db:seed○ 6: DB のコンテナ化完了
- これで、DB のコンテナ化が完了です。
- DBコンテナに入って実際に確認したい場合は、次の通り操作してみてください。
bash(省略可)# DB コンテナに入る $ docker-compose exec db ash # PostgreSQL に登録されている Sample App のデータベースに接続 $ psql -U root -d sample_app_development # テーブル生成の確認 sample_app_development=$ \dt sample_app_development=$ exit□ メールサーバのコンテナ化
■ 現状の問題点
- 特にありませんが、一つのコマンド打つだけで見れるのですから、あったら便利ですよね。
また、メールのデータも永続化できますが、ここで残すメリットを感じられなかったため、実装していません。永続化したい方は、参考URL等を確認しつつ実装してみてください。要領は前項までと同じです。
参考URL
■ 解決策
○ 1: docker-compose.yml の編集
docker-compose.ymlversion: '3' services: datastore: image: busybox volumes: - bundle_install:/usr/local/bundle - db_data:/var/lib/postgresql/data db: image: postgres:12.2-alpine volumes: - db_data:/var/lib/postgresql/data ports: - 5432:5432 environment: POSTGRES_USER: root POSTGRES_PASSWORD: pass app: build: context: . dockerfile: ./docker/Dockerfile ports: - 3000:3000 command: bundle exec rails s -p 3000 -b 0.0.0.0 volumes: - bundle_install:/usr/local/bundle - ./:/app:cached depends_on: - db environment: APP_DATABASE_HOST: db APP_DATABASE_USERNAME: root APP_DATABASE_PASSWORD: pass stdin_open: true tty: true + smtp: + image: mailhog/mailhog + ports: + - '8025:8025' volumes: bundle_install: db_data:○ 2: 開発環境のメール設定を編集
config/environments/development.rb... # Don't care if the mailer can't send. config.action_mailer.raise_delivery_errors = true - config.action_mailer.delivery_method = :test - host = 'railstutorial-yasulab.c9users.io' - config.action_mailer.default_url_options = { host: host, protocol: 'https' } + config.action_mailer.delivery_method = :smtp + config.action_mailer.smtp_settings = { + address: "smtp", + port: "1025", + } + config.action_mailer.default_url_options = { host: "localhost:3000" } ...○ 3: メールサーバのコンテナ化完了
- これで、メール確認が容易になりました。試しに SignUp してみるとアクティベーションメールが以下のURLに届きます。
- http://localhost:8025
□ 特記事項
■ 注意事項
- 今のところ特になし
■ 使用イメージ、ライブラリ
- ruby - Docker Hub
- busybox - Docker Hub
- postgres - Docker Hub
- mailhog/MailHog: Web and API based SMTP testing
- mysql - Docker Hub
□ おまけ: DB を MySQL にしたい場合
○ 1: Dockerfile の編集
docker/Dockerfile... ENV BUILD_PACKAGES="build-base" \ - DB_PACKAGES="postgresql-dev" \ + DB_PACKAGES="mysql-client mysql-dev" \ RAILS_PACKAGES="tzdata nodejs imagemagick" \ FAVORITE_PACKAGES="less" ...○ 2: docker-compose.yml の編集
docker-compose.yml... + db: + image: mysql:5.7 + volumes: + - db_data:/var/lib/mysql + ports: + - 3306:3306 + environment: + MYSQL_ROOT_PASSWORD: pass - db: - image: postgres:12.2-alpine - volumes: - - db_data:/var/lib/postgresql/data - ports: - - 5432:5432 - environment: - POSTGRES_USER: root - POSTGRES_PASSWORD: pass ...○ 3: config/database.yml の編集
config/database.ymldefault: &default adapter: mysql2 charset: utf8mb4 collation: utf8mb4_bin encoding: utf8mb4 pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> host: <%= ENV.fetch("APP_DATABASE_HOST") { '127.0.0.1' } %> port: <%= ENV.fetch("APP_DATABASE_PORT") { '3306' } %> username: <%= ENV.fetch("APP_DATABASE_USERNAME") { 'root' } %> password: <%= ENV.fetch("APP_DATABASE_PASSWORD") { 'pass' } %> development: <<: *default database: sample_app_development test: <<: *default database: sample_app_test production: <<: *default database: sample_app_production○ 4: Gemfile の編集
Gemfile... - gem 'pg', '0.18.4' + gem 'mysql2', '~> 0.4.4' ...
- 参考URL
- MySQL の charset 等について、上記の通り設定しない場合は以下の問題が発生するため、utf8mb4 を設定しておく( Rails のデフォルトだと utf8 になる )。
- Rail5.1 だと、mysql2 のバージョンを指定する必要がある。
docker-compose stop
とstart
なら作業状態を継続できることから、Gem もインストール済で作業を継続できる。 ↩
- 投稿日:2020-02-20T20:07:03+09:00
Railsで投稿した内容をGoogleMap上にmarkerで表示させる方法
はじめに
世界中の絶景を投稿できるアプリが作れます。
マップの表示方法はGoogleMapsAPIを使いました。
投稿した内容から自動的に軽度と緯度を取得してGoogleMapsAPIのマップ上に表示させます。
完成形はこんな感じ
環境
- Ruby 2.5.1
- Rails 5.2.3
- MySQL 5.6
目次
- GoogleMapsAPIのKEYを取得
- 簡単なアプリを作成しとりあえずGoogleMapsをアプリ上に表示させる
- 投稿機能を作り軽度、緯度取得し、マップ上に表示
GoogleMapsのKEYを取得
(グーグルアカウントにログインしてから始めてください)
まずGoogle Map PlatformからKEYを取得します
全て選択し続行。
プロジェクト名はなんでもいいですが今回はMyProjectを選択
支払い方法を入力します。
topページに移動しますので下の方のMaps Platfomを選択
Maps JavaScript APIを選択し有効を押します
画面上にあるAPI とサービスの認証情報
認証情報を作成を選択しAPIを作成。
画面にでてくるのがAPIキーになるので保存しといてください。(必要な場合はキーの制限をしてください)簡単なアプリを作成しgooglemapをアプリ上に表示させる
アプリ名はここではgmapにします
ここの部分は自分のアプリ名にしてください。
まずアプリの雛形とデータベース設計しますターミナル上で
rails _5.2.3_ new gmap -d mysql cd gmap rails g controller maps rails db:create rails db:migrateerbになっているのでhamlに変換します。
ここは任意で大丈夫です。
※erbをhamlに簡単に変えてくれるサイトですGemfilegem "haml-rails", ">= 1.0", '<= 2.0.1'bundel install rails haml:erb2haml
マップを表示させます
rails5.2以上なので今回はcredentialsを使いますroutes.rbRails.application.routes.draw do root to: "maps#index" endviews/maps/index.html.haml%div{:style => "width: 100%;"} #map{:style => "width: 100%; height: 100vh;"} :javascript function initMap(){ let map = new google.maps.Map(document.getElementById('map'), { center: {lat: -34.397, lng: 150.644}, zoom: 8 }); } %script{:src => "https://maps.googleapis.com/maps/api/js?key=#{Rails.application.credentials[:GOOGLE_MAP_KEY]}&callback=initMap"}先ほどのAPIキーをcredentialsで隠しておきます
ターミナル上でEDITOR="vi" bin/rails credentials:editGOOGLE_MAP_KEY: MyAPIKeyMyAPIKeyの部分は先ほど取得したkeyを入力してください
これで一応マップ表示ができたと思います!
投稿機能を作り軽度、緯度取得
まずは投稿機能を作るためにpostsのviewとcontroller,modelを作ります。
rails g scaffold posts name:string description:string latitude:float longitude:float rake db:migrate表示をgmaps4railsのgemを使って表示する方法に変えます。
Gemfilegem 'gmaps4rails'underscore、gmaps/googleをapplicaton.jsに追加します。
//= require underscore //追加 //= require gmaps/google //追加 //= require_tree .app/assets/javascripts/underscore.jsを作り
こちらの中身を全部コピペviews/maps/index.html.haml%div{:style => "width: 100%;"} #map{:style => "width: 100%; height: 100vh;"} :javascript handler = Gmaps.build('Google'); handler.buildMap({ provider: {mapTypeId: 'hybrid'}, internal: {id: 'map'} }, function(){ markers = handler.addMarkers(#{raw @hash.to_json}) handler.bounds.extendWith(markers); handler.fitMapToBounds(); handler.getMap().setCenter(new google.maps.LatLng(35.681298, 139.7640582)); handler.getMap().setZoom(4); });provider: {mapTypeId: 'hybrid'}の部分でマップのタイプを変えることができます。
何もなければ最初のタイプです。
他のタイプは下記を参照してください。ROADMAP 道路や建物などが表示される地図です
SATELLITE 衛星写真を使った地図です
HYBRID ROADMAPとSATELLITEの複合した地図です
TERRAIN 地形情報を使った地図ですapplicationにkeyを移してどこでも使えるようにします。
application.html.haml!!! %html %head %meta{:content => "text/html; charset=UTF-8", "http-equiv" => "Content-Type"}/ %title Gmap = csrf_meta_tags = csp_meta_tag = stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' = javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %script{:src => "//maps.google.com/maps/api/js?key=#{Rails.application.credentials[:GOOGLE_MAP_KEY]}"} %script{:src => "//cdn.rawgit.com/mahnunchik/markerclustererplus/master/dist/markerclusterer.min.js"} = yieldコントローラーに投稿した場所にmarkerが立つようにします。
maps_controller.rbclass MapsController < ApplicationController def index @posts = Post.all @hash = Gmaps4rails.build_markers(@post) do |post, marker| marker.lat post.latitude marker.lng post.longitude marker.infowindow post.description end end endmarker.infowindow post.descriptionのところに表示させたいカラムを変えれば表示内容を変えれます。
今回はdescriptionにします。これでマーカーが立つようになりました。
あとは新規投稿で軽度、緯度を取得できるようにしますgem 'geocoder'post.rbclass Post < ApplicationRecord geocoded_by :name after_validation :geocode end名前のカラム名で軽度と緯度が取得できるようになったので
あとは送信する時に余計なカラムを消しておきます。posts/_form.html.haml.field = f.label :name = f.text_field :name .field = f.label :description = f.text_field :description .field = f.label :latitude = f.text_field :latitude .field = f.label :longitude = f.text_field :longitude .actions = f.submit 'Save'のlatitudeとlongitudeの項目を消します。
これで完成です!最後に
間違っているところがあったら教えてください…
- 投稿日:2020-02-20T20:03:59+09:00
RailsアプリをDockerで作ってCircleCIで自動テストしてHerokuにデプロイした話
はじめに
どうも、Pirikaraです。
久しぶりの投稿となりました。今回は、Docker環境でRailsアプリケーションを開発し、
CircleCIで自動テスト、Herokuにデプロイするところまでやっていきたいと思います。
個人開発でやってみましたが、このエラーの山々......
一つ一つエラーを解決して設定ファイルを修正してを繰り返し繰り返し......
やっとまともに動くようになりました。
初めてやるよって方は僕の屍を踏み越えていってください。
ちなみに間違ってるよーとか改善点とかご指摘いただけると幸いです。環境
・Mac OS
・Ruby 2.5.3
・Rails 5.2.2
・MySQL 5.7また、Dockerがインストールされていることを前提としています(Dockerコマンドが使用できる状態)。
インストールしていない場合は、公式サイトからアカウントを作ってログインし、DockerHubからダウンロード・インストールします。
DockerhubHerokuについても登録していない場合は登録の必要があります。
公式サイトから登録が可能です。
Herokuアプリの構成
開発環境からDockerを導入し、RailsとMySQLのimageでコンテナを作りました。苦労していた環境構築がすぐに出来て感動。
GithubにpushするとCircleCIによる自動テストが行われて、テストをパスするとHerokuに自動でデプロイされる仕様です。
また、Herokuでは有料であればMySQLが使用できるみたいですが、僕はお金がないので本番環境のみPostgreSQLを使用します。では、
1. Docker開発環境の構築
2. CircleCIの導入と自動テスト
3. Herokuに自動デプロイ
の順番でやっていきましょう。1.Docker開発環境の構築
まず、アプリケーションのディレクトリを作成します。
ここではsample_appとします。sample_appディレクトリを作成 $ mkdir sample_app sample_appディレクトリに移動 $ cd sample_appディレクトリ内に「Dockerfile」「docker-compose.yml」「Gemfile」「Gemfile.lock」の4ファイルを作成します。
$ touch Dockerfile $ touch docker-compose.yml $ touch Gemfile $ touch Gemfile.lockでは、それぞれ中身を書いていきましょう。(Gemfile.lockは空のままです)
DockerfileFROM ruby:2.5.3 #必要なパッケージのインストール RUN apt-get update -qq && \ apt-get install -y build-essential \ libpq-dev \ nodejs #作業ディレクトリの作成 RUN mkdir /bbs_app #作業ディレクトリをAPP_ROOTに割り当てる ENV APP_ROOT /bbs_app WORKDIR $APP_ROOT #ローカルのGemfileを追加 ADD ./Gemfile $APP_ROOT/Gemfile ADD ./Gemfile.lock $APP_ROOT/Gemfile.lock #Gemfileのbundle installを実行 RUN bundle install ADD . $APP_ROOTdocker-compose.ymlversion: '3' services: db: image: mysql:5.7 environment: MYSQL_ROOT_PASSWORD: password MYSQL_DATABASE: root ports: - "4306:3306" web: build: . command: rails s -p 3000 -b '0.0.0.0' environment: RAILS_ENV: development volumes: - .:/bbs_app ports: - "3000:3000" links: - dbGemfilesource 'https://rubygems.org' gem 'rails', '5.2.2'ところどころ違いますが、
設定の内容についてはこちらの記事が詳しくて参考になったので、任せます。
DockerでRuby on Railsの環境構築を行うためのステップ【Rails 6対応】ここまで書けたら、dockerコマンドでrails newします。
$ docker-compose run web rails new . --force --database=mysql --skip-bundleあとでimageを構築する際にDockerfileにしたがってbundle installが実行されるので、ここではスキップします。
作成されたconfig/database.ymlを編集します。
database.ymldefault: &default adapter: mysql2 encoding: utf8 pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> username: root password: password # docker-compose.ymlのMYSQL_ROOT_PASSWORD host: db # docker-compose.ymlのservice名dockerを起動します。
imageを構築(コンテナは作成しない) $ docker-compose build コンテナを構築・起動 $ docker-compose upデータベースを作成します。
$ docker-compose run --rm web rails db:create--rmオプションをつけることで、コンテナが実行されたあと削除されます。
「rails g controller ~」や「rails console」など、docker-composeから始まるコマンドで実行していくことになりますが、
--rmをつけていないと実行するたびにコンテナが増えていってしまうので、都度削除しています。僕は。ここまでできると、localhost:3000へアクセスしてサーバーの起動を確認することができます。おめでとう。
2.CircleCIの導入と自動テスト
こちらのブログをパク・・・参考にしました。
導入まで大変わかりやすく、参考になりました。こちらを参考に導入してみてください。「.circleci」というディレクトリを作成し、これ以下に「config.yml」というファイルを作成します。
また、configディレクトリ以下に「database.yml.ci」というファイルを作成します。では、作成したファイルの中身を書いていきましょう。
config.ymlversion: 2 jobs: build: docker: - image: circleci/ruby:2.5.3-node-browsers environment: - BUNDLER_VERSION: 2.0.2 - RAILS_ENV: 'test' - image: circleci/mysql:5.7 environment: - MYSQL_ALLOW_EMPTY_PASSWORD: 'true' - MYSQL_ROOT_HOST: '127.0.0.1' working_directory: ~/bbs_app steps: - checkout - restore_cache: keys: - v1-dependencies-{{ checksum "Gemfile.lock" }} - v1-dependencies- - run: name: install dependencies command: | gem install bundler -v 2.0.2 bundle install --jobs=4 --retry=3 --path vendor/bundle - save_cache: paths: - ./vendor/bundle key: v1-dependencies-{{ checksum "Gemfile.lock" }} # Database setup - run: mv ./config/database.yml.ci ./config/database.yml # Database setup - run: name: Databasesetup command: | bundle exec rake db:create bundle exec rake db:schema:load # run tests! - run: name: Run rspec command: | mkdir /tmp/test-results TEST_FILES="$(circleci tests glob "spec/**/*_spec.rb" | \ circleci tests split --split-by=timings)" bundle exec rspec \ --format progress \ --format RspecJunitFormatter \ --out /tmp/test-results/rspec.xml \ --format progress \ $TEST_FILES # collect reports - store_test_results: path: /tmp/test-results - store_artifacts: path: /tmp/test-results destination: test-resultsdatabase.yml.citest: adapter: mysql2 encoding: utf8 pool: 5 username: 'root' port: 3306 host: '127.0.0.1' database: app_test #database.ymlのテスト環境のデータベース名を参照これでGithubへpushすると、自動でテストが実行されるようになります。
プルリクエストを作ってmasterブランチへmergeしていくと思うのですが、テストに失敗するとmergeすることができません。
ほらね。テストをパスすると、Mergeボタンがアクティブになります。
※ seeds.rbを読み込みたい場合
.circleci/config.ymlでseeds.rbの読み込み設定をしたところ、うまくいかなかったのでspec_helperにseeds.rbを読み込む設定をしました。
「database_cleaner」というgemを導入し、spec_helperへ処理を書いていきます。
これで、Rspec実行の際にデータベースがリフレッシュされ、seeds.fileが読み込まれます。Githubへpushした際に実行された場合は大丈夫なのですが、ローカルでdocker-composeコマンドからrspecを実行した場合にデータベースがリセットされてしまうので注意してください。
Gemfilegroup :development, :test do # rspec実行時にDBをリセットする gem 'database_cleaner' endspec_helper.rb# テスト実行時にDatabaseをリセットし、seeds.rbを読み込む RSpec.configure do |config| config.before(:suite) do DatabaseCleaner.strategy = :transaction DatabaseCleaner.clean_with(:truncation) Rails.application.load_seed end end3.Herokuに自動デプロイ
Herokuにデプロイするため、ファイルを修正・追加します。
まずはGemfile。
開発環境ではMySQL、本番環境ではPostgeSQLを使用するので、「pg」のgemをインストールします。Gemfile# 変更前 gem 'mysql2', '>= 0.4.4', '< 0.6.0' # 変更後 gem 'mysql2', '>= 0.4.4', '< 0.6.0', groups: %w(test development), require: false gem 'pg', '~> 0.19.0', group: :production, require: false次に、database.ymlを編集していきます。
本番環境でPostgreSQLを使う設定をします。database.ymlproduction: <<: *default adapter: postgresql encoding: unicode pool: 5そして、「heroku.yml」というファイルを新たに作成します。
作成場所はアプリ直下です。(GemfileとかDockerfileと一緒)
今回はDockerコンテナを使用して開発をしているので、コンテナごとHerokuのサーバーに持っていきます。
そのための設定ファイルです。知らんけど。DockerコンテナをHerokuにアップする方法は公式サイトを参考にしました。
heroku.ymlbuild: docker: web: Dockerfile run: web: bundle exec puma -C config/puma.rbアプリケーション側の設定はこれで完了です。
ではHeroku側の設定をしてデプロイしていきましょう。
ターミナルから、Herokuにログインします。$ heroku login # ログインするかどうか確認されます。 # クリックするとブラウザが立ち上がり、Herokuのログイン画面になります。ログインしてください。 heroku: Press any key to open up the browser to login or q to exit: # ログインが完了すると、ターミナルに「ログインしたよー」って表示されます。 Opening browser to https://cli-auth.heroku.com/auth/browser/************** Logging in... done Logged in as *******@email.com次に、Herokuにアプリケーションを作成します。
# アプリケーション名のところには好きな名前を入れてください。それでURLが作成されます。 $ heroku create アプリケーション名 Creating app... done, ⬢ ******* https://******.herokuapp.com/ | https://git.heroku.com/*******.gitちなみにこの時、Herokuのリポジトリが作成されています。
もしアプリケーションの名前を変更したいときは、Herokuの管理画面から名前を変更することに加えて、リポジトリを削除する必要があります。$ git remote rm herokuこのコマンドで消せます。
消さないとアプリ名とリポジトリ名が異なるためにデプロイできなくなります。そして、Heroku側でPostgreSQLを使用する設定をします。
$ heroku addons:create heroku-postgresql:hobby-dev # PostgreSQLのデータベース作ったよーって通知がきます。 Creating heroku-postgresql:hobby-dev on ⬢ ******... free Database has been created and is available ! This database is empty. If upgrading, you can transfer ! data from another database with pg:copy Created postgresql-transparent-70433 as DATABASE_URL Use heroku addons:docs heroku-postgresql to view documentation今回DockerコンテナをHerokuにのせるので、Heroku側でコンテナにアプリのStackをセットします。
$ heroku stack:set containerこの辺の話は「heroku.yml」の設定の時に出てきた公式サイトに全部載ってます。
Dockerを使用したデプロイこれでデプロイするための設定は完了しました。
masterブランチにアプリをpushしたのち、$ git push heroku masterこのコマンドでHerokuのリポジトリにpushすることでデプロイが完了します。エラーが起こらなければ。
さて、ついに自動デプロイです。
circleciを通してHerokuにデプロイしていくので、config.ymlファイルにdeployの処理を追加します。config.yml# 省略 - deploy: name: Deploy Master to Heroku command: | if [ "${CIRCLE_BRANCH}" == "master" ]; then git push https://heroku:$HEROKU_API_KEY@git.heroku.com/$HEROKU_APP_NAME.git master fi「HEROKU_API_KEY」と「HEROKU_APP_NAME」については、CircleCIに設定した環境変数が読まれるため、
こちらを設定していきます。どちらもHerokuの管理画面から取得できます。
アカウント設定からHEROKU_API_KEYに当たるAPI KEYを......
アプリケーションの設定からHEROKU_APP_NAMEを......
それぞれ取得します。CircleCIのアプリケーション管理画面から、右上の「Project Setting」をクリックします。
Environment Variablesから「Add Variables」をクリックして、先ほど取得した値を環境変数として設定します。
これがconfig.ymlで使用されます。
設定は以上です。
これで、Githubへpush → CircleCiが自動でテスト → パスすればHerokuに自動でデプロイ
という風に動きます。良かったね。
終わりに
DockerとCircleCIを利用したのは初めてでしたが、
環境構築もテスト・デプロイも簡潔になったので、非常に便利でした。特にテストコマンドやデプロイコマンドについては「コマンド打つだけやしなぁ......」と自動化の恩恵を甘く見ていましたが、
チリも積もればなんとやらです。
いちいちコマンドを打つ煩わしさから解放され、アプリの開発スピードも段違いだったように感じます。参考にしていただけたら幸いです。
またね。
- 投稿日:2020-02-20T19:53:02+09:00
【初心者向け】form_withでundefined method `form_with' forというエラーが出た際の対応方法について
Railsでform_withを使用した際、undefined method `form_with' for やDid you mean? form_tagと表示されてしまって詰まった話
Railsでアプリケーションを作成中、投稿フォームの実装時、form_withを使用したところ、永遠とエラーが出てしまって解決に時間がかかってしまったため、備忘録として残す。
結論、form_withはRailsのバージョンが5.1 以上じゃないと動かないようです。
Railsガイド様いつも参考にしています
https://railsguides.jp/5_1_release_notes.htmlWebcamp様にも記載ございました
https://web-camp.io/magazine/archives/17665何をすればいいのか?
Gemfileの中にあるRailsのgemのバージョンをアップグレードする
Railsバージョンアップデートの手順
- gemファイルのrailsを5.1以上で指定する(ここでは5.2.4を指定しています)
gem 'rails', '~> 5.2.4'
- bundle updateする(※注意:bundle installではない!!!!)
bundle updateRailsをバージョンアップした際にRails sで立ち上がらない問題が発生
undefined method `halt_callback_chains_on_return_false=' for ActiveSupport:Module (NoMethodError)これが表示されたら、configファイルの中身の以下
config/initializers/new_framework_defaults.rb以下をコメントアウト
ActiveSupport.halt_callback_chains_on_return_false = false と Rails.application.config.action_controller.raise_on_unfiltered_parameters = true上記をコメントアウトし、これで通常通りRails sで起動した。
(よかった・・・・でも勉強になった)ちなみに、ActiveSupport.halt_callback_chains_on_return_falseの意味はよくわかりません・・・。
https://techracho.bpsinc.jp/hachi8833/2017_02_21/35843参考記事
https://qiita.com/luglio22/items/0dd520f3748935bada81もし
ここは違う、ここはこうした方が良い等々ございましたらご指摘いただけますと幸いです。
最後までみていただき、ありがとうございます。
- 投稿日:2020-02-20T19:34:28+09:00
rails s 起動しない 解決方法の1つ
チーム開発においてエラーが発生!
※ 初心者向けに何か助けになればと思い記述しております。
※ アウトプットの練習も兼ねております。開発環境 rails 5.2.4.1
ruby 2.5.1
クラウドサービスはAWSを使用
現状自動デプロイまで完了5名でフリマアプリの作成
githubアプリより最新のタスクデータを取得のため
masterブランチにブランチを切り替えpullを実行rails sでサーバーを立ち上げた際に起動しない
..... => Rails 5.2.4.1 application starting in development => Run `rails server -h` for more startup options Exiting Traceback (most recent call last): 85: from bin/rails:3:in `<main> .....試した事
PC再起動
bunde linstall
rails db:migrate
rails db:rollback
rails db:drop
rails db:migration:reset
rake db:migrate:reset 等...いずれもエラー表示冷静にエラーの記述を見るとrails sの起動時に下記の内容が
config/initializers/carrierwave.rb:10:in `block in <top (required)>'
指定のファイルconfig/initialize/carriewave.rbの記述に注目
provider: 'AWS', aws_access_key_id: Rails.application.credentials[:aws] [:access_key_id], aws_secret_access_key: Rails.application.credentials[:aws] [:secret_access_key], region: 'ap-northeast-1'
結果awsのマスターkeyをpullでは取得できていないためであったと思われる。
configファイルの参加にmaster.keyのファイルを作成しマスターよりkeyを一旦取得し記述するとエラーは解決。しかしこのままではセキュリティに問題がある為、別の方法を模索中です。
- 投稿日:2020-02-20T19:13:49+09:00
処理速度が早いrender
renderは使い方を間違えると表示に読み込みが遅くなってしまう。
今回はパフォーマンスを意識して学習した。NG:each doで部分テンプレート読み込み
each doは繰り返し処理なので、部分テンプレートもなんども読み込みする。
悪い例# eachを使う処理はNG <% @products.each do |product| %> // 3つの書き方を記述しているが、each doを利用しているのでだめ <%= render product %> <%= render 'products/product', product: product %> <%= render partial: 'products/product', locals: { product: product } %> <% end %>@productsが1000個の場合、1000回部分テンプレートを読み込む事になる。
処理が早い書き方
正解= render partial: 'product', collection: @products, as: "product" # partial: 'product' > _product.html.erbを読み込む # collection: @products > 複数あるproductを1つずつ処理する。 > each doの代わりになる。 # as: "product" > @products の単体の変数 、部分テンプレートで利用する変数はproductになる。これだと一度しか部分テンプレートが読み込まれないから、処理速度が早まる。
- 投稿日:2020-02-20T18:41:37+09:00
rubocop-railsのfind_by_*系メソッドの自動修正をstaticに定義した一部のメソッドの上で無効にする
Railsで開発するにあたって、rubocop-railsをLinterとして利用しており、
自動修正機能を適用したときにエラーに繋ってしまったのでメモしておきます。背景
Railsでは
find_by_id
やfind_by_name
のように、ActiveModelのカラム名を利用して検索できるメソッドが動的に定義されます。しかし、このような個別のメソッドを利用するのではなくfind_by形式に統一するスタイルがRuboCopにおいては推奨されており、検知&自動修正するためのCopが存在します。
一方で、自前で
find_by_hoge
のようなメソッドを定義した場合にもこのCopの判定対象になってしまい、自動修正の対象になってしまいます。自分の開発しているプロジェクトでは自動修正をかけた際に意図せず変更されてしまい、メソッド未定義のエラーに繋りました。
回避方法
一部のメソッドをこのCopの対象から除外したい場合には以下のアプローチがあります:
- 該当行にコメントで
rubocop:disable Rails/DynamicFindBy
を追加するrubocop.yml
中で該当メソッド名をホワイトリストに追加する2つの方法があるなかで、私は後者の選択をしました。
判断の理由としては、前者のインラインコメントで都度対応をするという方法は
- 実装者がコメントの追加を忘れやすい(コードレビューでも指摘忘れやすい)
- 後からコードを読むメンバーにコメントの背景が伝わりづらい
- 呼びだしの箇所で個別に記述するコストがかかる
といったいくつかのデメリットがあると考えたためです。
そのため、後者の
.rubocop.yml
中に記述するアプローチを取りました。rubocop.ymlRails/DynamicFindBy: Whitelist: - find_by_sql - find_by_hogeWhitelistに該当メソッドだけでなく、
find_by_sql
についても追加していますが、こちらのメソッドもRailsの標準のメソッドであり自動修正の対象とはしたくないためです。RuboCopの設定は、デフォルトの設定を自動的に継承するようになっていますが、
継承されている設定と同じ項目についての設定を記述した時に以下のようなルールで上書きがされます。
- ハッシュマップはデフォルトの物とマージされ、衝突したキーについては最後の設定で上書きされる
- 配列については常に最後の設定によって上書きされる
(参考: Configuration - RuboCop: The Ruby Linter that Serves and Protects)
今回は後者の配列の上書きに該当しますので、標準でWhitelistに追加されているfind_by_sqlについても自分で追加しておく必要がありました。
- 投稿日:2020-02-20T17:49:10+09:00
えっ、環境変数ってコマンドの後ろに書いたらダメなんですか?
勘違いしていたこと
普段 Ruby on Rails で開発を行っているのですが、rails では以下のように、コマンドの後ろに環境変数を付けることができます。
# 環境変数 RAILS_ENV を一時的に "test" に変えてから DB を作成する。 $ bundle exec rails db:create RAILS_ENV=testまた、経験上 ↓ のように コマンドの前 に指定しても動くことも知っていました。
$ RAILS_ENV=test bundle exec rails db:createこのことから、僕は Linuxのシェルはコマンドの前か後ろに環境変数を指定できる ものだと思い込んでおり、 その日の気分で前につけたり後ろにつけたり してました。
あれ、なんかおかしいな
先日、以下のような ruby スクリプトに環境変数が渡せないことに気づきました。
env_test.rb#!/usr/bin/env ruby puts "APP_ENV: #{ENV['APP_ENV']}"$ ./env_test.rb APP_ENV=hoge APP_ENV:コマンドの前に環境変数を書いたらうまく渡せました。
$ APP_ENV=hoge ./env_test.rb APP_ENV: hoge調べてみると、bashで環境変数をexportせずにシェルスクリプトを実行したい場合はコマンドの前に記述することで代替できる という記事が。
えっ、後ろには書けないの・・・?なんで db:create とかは後ろに環境変数を書けるのか
でも db:create とか db:migrate は後ろに書けるじゃーん!なんでー!と思って更に調べました。
すると rails のコマンドを使うときに環境変数を後ろに書けるのは、rake が引数として
VAR=VALUE
を受け取り、それを環境変数にセットしてるから ということが分かりました。
https://github.com/ruby/rake/blob/master/doc/command_line_usage.rdocrake のおかげだったんだ・・・
まとめ
- rake タスクを実行するときは、環境変数をコマンドの前に書いても後ろに書いても良い
- それ以外の場合はコマンドの前に書く
使い分けるのも面倒なので、いっそのこと、常に前に書くようにしちゃってもいいのかなと思ってます。
- 投稿日:2020-02-20T17:18:37+09:00
ActionView::Template::Error (Autoprefixer doesn’t support Node v0.10.48. Update it.):
RailsアプリをEC2へデプロイする過程で遭遇したエラーです。調べるとnode.jsを入れ直せとのことなので、
$ sudo yum remove nodesこれで削除して
ActionView::Template::Error (No such file or directory - node):エラーメッセージが変わりました。新しく入れ直します。
$ curl --silent --location https://rpm.nodesource.com/setup_6.x | sudo bash - $ sudo yum install -y nodejsインストール完了です。
gem mini_racerstackoverflowではmini_racerをインストールするように書かれていたものもありました。自分はこれでは解決しませんでした。
- 投稿日:2020-02-20T15:08:52+09:00
rails db:migrateができないとき...
エラー
migrationファイル生成後、rails db:migrateとすると以下のようなエラーが出た。
$ rails db:migrate rails aborted! StandardError: An error has occurred, this and all later migrations canceled:試したこと
rails db:migrate:resetを実行すると...
rails db:migrate:reset Permission denied @ unlink_internal - C:/Users/自分/アプリ名/db/development.sqlite3 Couldn't drop database 'db/development.sqlite3' rails aborted! Errno::EACCES: Permission denied @ unlink_internal - C:/Users/自分/アプリ名/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)と
またエラーに...rails db:migrate:resetの代わりに、
$ rm db/development.sqlite3
$ bin/rails db:setup
を実行。$ bin/rails db:setup Database 'db/test.sqlite3' already exists Run `rails db:migrate` to update your database then try again. Created database 'db/development.sqlite3'再度rails db:migrateにトライすると、マイグレートされました。
- 投稿日:2020-02-20T14:46:53+09:00
新規プロジェクトをrailsで作成中、ズルしたら痛い目にあった
ある日
「あー!デプロイまで終わった!」
「次はいよいよ、ポートフォリオ作ってみようかなー!」
「うわ、またあの環境構築からか。。。面倒だなー」
「前に作ったサンプルサプリで設定したdocker-composeとか使おうかな。」
。。。。
「おおー、docker-compose build 、up、rails newまで早いな!」
「よしこのまま一回localhost接続みてみるかー。。ぽちぽちっと」
「あれ?エラーか。なんでだろ?」
「前みたいな設定にしてるはずだけど、何が違うんだろ??」
「あれ、publicの中身ないぞ!tmpも空っぽだ!これじゃnginxソケット通信できないぞ!どうしよーー」
「はぁ、諦めて一から環境構築するかああ。」何が問題だったのか?
1:無闇やたらにVOLUME使っていた。
2:初期ページのまま、production環境でlocalhost接続していた!
こんなこと普通ならないですよねー。笑
なぜこうなったかというと
* rails環境構築を省こうとした。
* 楽をしたかった。
この2点だけでした!今回はrailsの復習にもなりましたので、ここに記事を残して2度と同じミスがないようにしようと思っています!
では、本編どうぞー。1:無闇やたらにVOLUME使っていた。
このように、以前に作成したことがあったプロジェクトからrailsのdockerfile
docker-compose.yml
nginxのdockerfile
をコピペして使用したのですが、
VOLEME public
VOLUME tmp
がrailsのdockerfileに記述したままでして、
結果rails new
しても、publicとtmpの中身が生成されなかったです。
docker-composeは起動していましたが、localhost
に接続してもnginxが参照するsocketsはないし、welcomepageはそもそもないし。で、、そりゃエラーですよね!笑
解決方法
VOLEME public
VOLUME tmp
railsのdockerfileから記述を削除し、
docker-compose build
をやり直した所、無事解決にいたりlocalhost
接続できました。
[image:26B581B4-1DFB-4C9D-97FE-8BE8016A13BF-12763-00005F9EE8A93718/yay.png]初期ページのまま、production環境でlocalhost接続していた!
これは、誠にお恥ずかしい話ですが
developmentモードでwelcomeページが表示されたので、productionモードでも起動確認がしたくなり、初期ページのままいじってました。
当然docker-compose up
してもlocalhost
にwelcomeページなんか出ないはずなのに(welcomepageがあるのはdevelopmentモードのみ!)
あれ?出ないなぁと思っていました!解決策
サンプルページを作成
docker-compose run app bash rails g controller home index
接続を確認
localhost
で無事表示される。疑問点
dockerfileの
VOLEME public
VOLUME tmp
記述が、なぜファイルの中身を生成しなかったのか、まだわかってないので調べます!
分かる方いましたら教えてください!
- 投稿日:2020-02-20T14:28:44+09:00
あっさり読むrails⑤ (非同期処理 メニュー追加)
はじめに
以前の記事で非同期処理について書かせて頂きました。
今回は、その別バリエーションを書かせて頂きます。実行
前回は"フォームに入力した文字が別の場所に表示させる"コードについてお話しましたが、今回は"ボタンを押すとプルダウンメニューが表示される"コードについて書かせて頂きます。
まずは、元となるビューファイルです。
index.html.haml.btn 表示させる = form_with model:@sample, local: true do |f| .pulldown
btn
を押してpulldown
の所にプルダウンメニューを表示させるわけですね。CSSはとりあえず次の様に書いておけば、ボタンっぽくなるでしょう。
.btn{ background-color: #F999; padding: 5px; }次にJSのファイルです。とりあえず、「ボタンを押したら発火する」だけのコードを書きます。
$(".btn").on("click", function(){ });中にはなにも書いてませんので、追加するビューコードと追加するための処理を書いていきます。
$(".btn").on("click", function(){ let Form = ` <form action = "/" accept-charset = "UTF-8" method = "POST" class = "menu"> <select class = "pulldown__menu" name = "menu"> ` $(".pulldown").html(Form); });この時クラス名を間違えると、プルダウンメニューが追加されない、CSSが適用されないなどの不具合が起きますので、
慎重に記述してください。これでプルダウンメニューが追加されるようになりますが、今は中身が空っぽなので何も選ぶ事はできません。
そのため、プルダウンメニューの中身を追加するコードを書いていきます。(前略) var list = [ "--", "東", "西", "南", "北"]; let Form2 = ` ${list.forEach(function(value){ let wrapper = `<option value>${value}</option>` $(".pulldown__menu").append(wrapper); }) } ` (後略)(注意)無駄な記述があるコードになっています。
修正でき次第、更新します。全体の流れとしては、
①ボタンをクリックしたらフォームを表示させる(html)
②メニューで表示させる値を配列で用意する(list)
③メニューの項目一つ一つを、eachメソッドで追加(append)していくとなります。
全体のJSコードは次の通りです。
$(function(){ $(".btn").on("click", function(){ let Form = ` <form action = "/" accept-charset = "UTF-8" method = "POST" class = "menu"> <select class = "pulldown__menu" name = "menu"> ` $(".pulldown").html(Form); var list = [ "--", "東", "西", "南", "北"]; let Form2 = ` ${list.forEach(function(value){ let wrapper = `<option value>${value}</option>` $(".pulldown__menu").append(wrapper); }) } ` }); });
- 投稿日:2020-02-20T13:12:35+09:00
#Rails の ActiveRecord で DBの値が nil / NULL かどうかを モデルのインスタンスに問うメソッド
- 投稿日:2020-02-20T13:12:33+09:00
#Rails ActiveRecord aks instance to column presence or nil ? merthod / Model#name?
user = User.first # When presence user.update!(name: "xxx") # user.name? => true # When blank user.update!(name: "") user.name? # => true # When nil User.first.name? user.name? # => false# Original by Github issue https://github.com/YumaInaura/YumaInaura/issues/2996
- 投稿日:2020-02-20T11:11:08+09:00
#Rails deny nill value but allow blank value validation / exclusion: { in: [nil] }
Model
class User < ApplicationRecord validates :name, exclusion: { in: [nil] } end動作イメージ
User.create!(name: nil) ActiveRecord::RecordInvalid: 名前 は予約されています User.create!(name: "") # 成功ガイド
https://railsguides.jp/active_record_validations.html
presence は?
空文字を禁止してしまう
2.9 presence
このヘルパーは、指定された属性が「空でない」ことを確認します。値がnilや空文字でない(つまり空欄でもなければホワイトスペースでもない)ことを確認するために、内部でblank?メソッドを使っています。class Person < ApplicationRecord validates :name, :login, :email, presence: true endallow_blank は?
値が未入力、空文字の時にバリデーションをスキップするためオプション指定であり、空文字を許容するわけではない
3.2 :allow_blank
:allow_blankオプションは:allow_nilオプションと似ています。このオプションを指定すると、属性の値がblank?に該当する場合(nilや空文字など)にバリデーションがパスします。class Topic < ApplicationRecord validates :title, length: { is: 5 }, allow_blank: true end Topic.create(title: "").valid? # => true Topic.create(title: nil).valid? # => trueOriginal by Github issue
- 投稿日:2020-02-20T11:05:30+09:00
[Ruby] irb や pry で例外時のバックトレースを表示する
TL;DR
irb や pry で例外時のバックトレースを表示するには
バージョン情報
$ ruby -v ruby 2.6.5p114 (2019-10-01 revision 67812) [x86_64-darwin18] $ bin/rails -v Rails 6.0.1実例
以下のモデルを作成した。
app/models/hunter.rbclass Hunter < ApplicationRecord validates name, presence: true end早速
bin/rails c
で動作を確認してみると謎のエラーが発生した。irb(main):008:0> Hunter.create!(name: 'クラピカ') Traceback (most recent call last): 1: from (irb):8 NoMethodError (undefined method `Hunter' for #<Hunter:0x00007f8a322f52d8>)Hunter#Hunter というメソッドがないって言われる……。なんだこの HUNTER×HUNTER みたいなへんちくりんなエラーは ?
それにしても、他に情報が何も出力されないのでエラーの原因がさっぱりわからない。こういうときはメソッドの呼び出し状況であるバックトレースを出力する。Ruby では $@ という特殊変数にバックトレースを参照することができる。
irb(main):002:0> puts $@ /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activemodel-6.0.1/lib/active_model/attribute_methods.rb:431:in `method_missing' /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activemodel-6.0.1/lib/active_model/validator.rb:150:in `block in validate' /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activemodel-6.0.1/lib/active_model/validator.rb:149:in `each' /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activemodel-6.0.1/lib/active_model/validator.rb:149:in `validate' # 略 /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activerecord-6.0.1/lib/active_record/persistence.rb:55:in `create!' (irb):8:in `irb_binding' # 略 -e:1:in `<main>' => nilバックトレースに
validate
というキーワードがあったのでバリデーション周りを疑ってみる。すると本来シンボルであるはずの validates の引数がシンボルになっていなかったのが原因だと判明した!app/models/hunter.rbclass Hunter < ApplicationRecord # name が self.name すなわち Hunter.name と解釈されて "Hunter" を返していた。 # name を :name に修正した。 validates :name, presence: true endしかしこの方法はちょっと不便だ。もう一度
$@
を参照しようとすると空になってしまう。irb(main):002:0> puts $@ /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activemodel-6.0.1/lib/active_model/attribute_methods.rb:431:in `method_missing' /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activemodel-6.0.1/lib/active_model/validator.rb:150:in `block in validate' /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activemodel-6.0.1/lib/active_model/validator.rb:149:in `each' /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activemodel-6.0.1/lib/active_model/validator.rb:149:in `validate' # 略 /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activerecord-6.0.1/lib/active_record/persistence.rb:55:in `create!' (irb):8:in `irb_binding' # 略 -e:1:in `<main>' => nil irb(main):003:0> puts $@ => nilそこで irb をより強力にしたツールである pry を使う。Gemfile に
pry-rails
を追加して bundle install しておく。Gemfilegroup :development, :test do gem 'pry-rails' end
bin/rails c
で動作を確認してみる。[1] pry(main)> Hunter.create!(name: 'クラピカ') NoMethodError: undefined method `Hunter' for #<Hunter:0x00007fe654afd088> from /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activemodel-6.0.1/lib/active_model/attribute_methods.rb:431:in `method_missing'
wtf
と入力すると、例外のバックトレースが表示される。すべてではなく先頭から数行が表示される。[2] pry(main)> wtf Exception: NoMethodError: undefined method `Hunter' for #<Hunter:0x00007fe654afd088> -- 0: /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activemodel-6.0.1/lib/active_model/attribute_methods.rb:431:in `method_missing' 1: /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activemodel-6.0.1/lib/active_model/validator.rb:150:in `block in validate' 2: /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activemodel-6.0.1/lib/active_model/validator.rb:149:in `each' 3: /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activemodel-6.0.1/lib/active_model/validator.rb:149:in `validate' 4: /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activesupport-6.0.1/lib/active_support/callbacks.rb:429:in `block in make_lambda'もう一度入力してもバックトレースが表示される。おもしろいことに
wtf!
やwtf!!?
のようにwtf
の後に!
や?
をつけると、つけた量に比例して表示されるバックトレースの行数が増える。[3] pry(main)> wtf! Exception: NoMethodError: undefined method `Hunter' for #<Hunter:0x00007fe654afd088> -- 0: /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activemodel-6.0.1/lib/active_model/attribute_methods.rb:431:in `method_missing' 1: /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activemodel-6.0.1/lib/active_model/validator.rb:150:in `block in validate' 2: /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activemodel-6.0.1/lib/active_model/validator.rb:149:in `each' 3: /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activemodel-6.0.1/lib/active_model/validator.rb:149:in `validate' 4: /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activesupport-6.0.1/lib/active_support/callbacks.rb:429:in `block in make_lambda' 5: /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activesupport-6.0.1/lib/active_support/callbacks.rb:201:in `block (2 levels) in halting' 6: /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activesupport-6.0.1/lib/active_support/callbacks.rb:607:in `block (2 levels) in default_terminator' 7: /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activesupport-6.0.1/lib/active_support/callbacks.rb:606:in `catch' 8: /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activesupport-6.0.1/lib/active_support/callbacks.rb:606:in `block in default_terminator' 9: /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activesupport-6.0.1/lib/active_support/callbacks.rb:202:in `block in halting'バックトレースを最後まで表示したい場合は
wtf -v
と入力する。[4] pry(main)> wtf -v Exception: NoMethodError: undefined method `Hunter' for #<Hunter:0x00007fe654afd088> -- 0: /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activemodel-6.0.1/lib/active_model/attribute_methods.rb:431:in `method_missing' 1: /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activemodel-6.0.1/lib/active_model/validator.rb:150:in `block in validate' 2: /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activemodel-6.0.1/lib/active_model/validator.rb:149:in `each' 3: /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activemodel-6.0.1/lib/active_model/validator.rb:149:in `validate' # 略 73: /Users/killua/.rbenv/versions/2.6.5/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54:in `require' 74: /Users/killua/.rbenv/versions/2.6.5/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54:in `require' 75: -e:1:in `<main>'参考
- 投稿日:2020-02-20T11:05:30+09:00
[Ruby][初心者向け] コンソールで例外が発生したときは、バックトレースを表示して原因を調べよう
typo など特に凡ミス対策に有効です ?
TL;DR
- コンソール (irb や pry) で例外が発生しても、バックトレースが表示されないので原因がわからないことがある。
- コンソールで例外時のバックトレースを表示するには
バージョン情報
$ ruby -v ruby 2.6.5p114 (2019-10-01 revision 67812) [x86_64-darwin18] $ bin/rails -v Rails 6.0.1実例
以下のモデルを作成した。
app/models/hunter.rbclass Hunter < ApplicationRecord validates name, presence: true end早速
bin/rails c
(irb) で動作を確認してみると謎のエラーが発生した。irb(main):008:0> Hunter.create!(name: 'クラピカ') Traceback (most recent call last): 1: from (irb):8 NoMethodError (undefined method `Hunter' for #<Hunter:0x00007f8a322f52d8>)Hunter#Hunter というメソッドがないって言われる……。なんだこの HUNTER×HUNTER みたいなへんちくりんなエラーは ?
それにしても、他に情報が何も出力されないのでエラーの原因がさっぱりわからない。こういうときはメソッドの呼び出し状況であるバックトレースを出力する。Ruby では $@ という特殊変数でバックトレースを参照することができる。
irb(main):002:0> puts $@ /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activemodel-6.0.1/lib/active_model/attribute_methods.rb:431:in `method_missing' /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activemodel-6.0.1/lib/active_model/validator.rb:150:in `block in validate' /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activemodel-6.0.1/lib/active_model/validator.rb:149:in `each' /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activemodel-6.0.1/lib/active_model/validator.rb:149:in `validate' # 略 /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activerecord-6.0.1/lib/active_record/persistence.rb:55:in `create!' (irb):8:in `irb_binding' # 略 -e:1:in `<main>' => nilバックトレースに
validate
というキーワードがあったのでバリデーション周りを疑ってみる。すると本来シンボルであるはずの validates の引数がシンボルになっていなかったのが原因だと判明した!app/models/hunter.rbclass Hunter < ApplicationRecord # name が self.name すなわち Hunter.name と解釈されて "Hunter" を返していた。 # name を :name に修正した。 validates :name, presence: true endしかしこの方法はちょっと不便だ。もう一度
$@
を参照しようとすると空になってしまう。irb(main):002:0> puts $@ /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activemodel-6.0.1/lib/active_model/attribute_methods.rb:431:in `method_missing' /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activemodel-6.0.1/lib/active_model/validator.rb:150:in `block in validate' /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activemodel-6.0.1/lib/active_model/validator.rb:149:in `each' /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activemodel-6.0.1/lib/active_model/validator.rb:149:in `validate' # 略 /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activerecord-6.0.1/lib/active_record/persistence.rb:55:in `create!' (irb):8:in `irb_binding' # 略 -e:1:in `<main>' => nil irb(main):003:0> puts $@ => nilそこで irb をより強力にしたツールである pry を使う。Gemfile に
pry-rails
を追加して bundle install しておく。Gemfilegroup :development, :test do gem 'pry-rails' end
bin/rails c
で動作を確認してみる。[1] pry(main)> Hunter.create!(name: 'クラピカ') NoMethodError: undefined method `Hunter' for #<Hunter:0x00007fe654afd088> from /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activemodel-6.0.1/lib/active_model/attribute_methods.rb:431:in `method_missing'
wtf
と入力すると、例外のバックトレースが表示される。すべてではなく先頭から数行が表示される。[2] pry(main)> wtf Exception: NoMethodError: undefined method `Hunter' for #<Hunter:0x00007fe654afd088> -- 0: /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activemodel-6.0.1/lib/active_model/attribute_methods.rb:431:in `method_missing' 1: /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activemodel-6.0.1/lib/active_model/validator.rb:150:in `block in validate' 2: /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activemodel-6.0.1/lib/active_model/validator.rb:149:in `each' 3: /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activemodel-6.0.1/lib/active_model/validator.rb:149:in `validate' 4: /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activesupport-6.0.1/lib/active_support/callbacks.rb:429:in `block in make_lambda'もう一度入力してもバックトレースが表示される。おもしろいことに
wtf!
やwtf!!?
のようにwtf
の後に!
や?
をつけると、つけた量に比例して表示されるバックトレースの行数が増える。[3] pry(main)> wtf! Exception: NoMethodError: undefined method `Hunter' for #<Hunter:0x00007fe654afd088> -- 0: /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activemodel-6.0.1/lib/active_model/attribute_methods.rb:431:in `method_missing' 1: /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activemodel-6.0.1/lib/active_model/validator.rb:150:in `block in validate' 2: /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activemodel-6.0.1/lib/active_model/validator.rb:149:in `each' 3: /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activemodel-6.0.1/lib/active_model/validator.rb:149:in `validate' 4: /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activesupport-6.0.1/lib/active_support/callbacks.rb:429:in `block in make_lambda' 5: /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activesupport-6.0.1/lib/active_support/callbacks.rb:201:in `block (2 levels) in halting' 6: /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activesupport-6.0.1/lib/active_support/callbacks.rb:607:in `block (2 levels) in default_terminator' 7: /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activesupport-6.0.1/lib/active_support/callbacks.rb:606:in `catch' 8: /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activesupport-6.0.1/lib/active_support/callbacks.rb:606:in `block in default_terminator' 9: /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activesupport-6.0.1/lib/active_support/callbacks.rb:202:in `block in halting'バックトレースを最後まで表示したい場合は
wtf -v
と入力する。[4] pry(main)> wtf -v Exception: NoMethodError: undefined method `Hunter' for #<Hunter:0x00007fe654afd088> -- 0: /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activemodel-6.0.1/lib/active_model/attribute_methods.rb:431:in `method_missing' 1: /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activemodel-6.0.1/lib/active_model/validator.rb:150:in `block in validate' 2: /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activemodel-6.0.1/lib/active_model/validator.rb:149:in `each' 3: /Users/killua/myapp/vendor/bundle/ruby/2.6.0/gems/activemodel-6.0.1/lib/active_model/validator.rb:149:in `validate' # 略 73: /Users/killua/.rbenv/versions/2.6.5/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54:in `require' 74: /Users/killua/.rbenv/versions/2.6.5/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54:in `require' 75: -e:1:in `<main>'参考
- 投稿日:2020-02-20T11:02:09+09:00
ActiveAdminのTips
最近ActiveAdminを使用しているのですが
便利な反面, 仕様がわかりづらく, バグでは?と思うことが多いです.
私が躓いたところをいろいろ追加して行こうと思います.前提環境
activeadmin (1.4.3)詳細画面
if文の挙動
特定のカラムに値があったら, 表示する.
なければ表示しない. という単純な仕様shops.rbActiveAdmin.register Shop do show do panel "求人(Job)情報" do attributes_table_for shop.job do #① idがあれば表示 if resource.job.job_img1_id.present? row :job_img1_id end #② idがなければ表示 if resource.job.job_img2_id.nil? row :job_img2_id end #③ idがあれば表示 (ここ挙動がおかしい!!バグ!!) if :job_img3_id.present? row :job_img3_id end #④ idがなければ表示 (ここ挙動がおかしい!!バグ!!) if :job_img4_id.nil? row :job_img4_id end end end end end今 job_img[1-4]_id は値がないnull(nil)です.
つまり、写真2と写真4だけが表示されているのが正しい挙動です.実際の画面を見ると
写真1 写真4 => 非表示
写真2 写真3 => 表示
(上下のメイン写真コメントと写真5はここでは無関係)ここで
写真3は写真1同様に非表示とならなければなりません.同様に
写真4は写真2同様に表示とならなければなりません.
ここでバグがありました.結論
:シンボル使用した時と, resouceから呼び出した時で, 挙動が正反対になっています.
正しい挙動はresouceを使用した時です.
ifの引数にシンボリックの使用は推奨されません.
- 投稿日:2020-02-20T11:02:09+09:00
ActiveAdminのTips& Bugs
最近ActiveAdminを使用しているのですが
便利な反面, 仕様がわかりづらく, バグでは?と思うことが多いです.
私が躓いたところをいろいろ追加して行こうと思います.
日本語の記事が見つからなかったので, 書きました.前提環境
activeadmin (1.4.3)一覧画面(Index)
カスタムフィルター(子テーブルの要素をFilter対象とする)
前提
親(Shop) : 子(Job) = 1 : N 関係の時,
親要素に子要素をJOINして, DBのビューのような使い方をしている.実現したいこと
子(Job)の特定のカラム(ここでは:published)の値でフィルター機能を作りたい
ソースコード
app/models/shop.rbclass Shop < ActiveRecord::Base has_many :jobs, dependent: :destroy # ActiveAdmin用のscope scope :join_jobs, -> { joins(:jobs)} scope :active_admin_custom_filter, -> (filter) { # gem ransackerのバグのため、ここで数値へ変換 # filter = filter == "掲載中" ? 1 :0 self.where('jobs.published = ? ',filter) } # 別にprivateでなくても良いが private # ActiveAdmin用のfilterカスタマイズ def self.ransackable_scopes(auth_object = nil) [:active_admin_custom_filter] endapp/admin/shops.rbActiveAdmin.register Shop do # gemのransackerにバグがあり, 自作scopeに数値を渡せない. initilizarの要修正 filter :active_admin_custom_filter, label: "掲載(求)", as: :select, collection: proc{ [%(掲載中 1), %w(掲載停止 0)] } # initilizarを修正していない時はこっち # filter :active_admin_custom_filter, label: "掲載(求)", as: :select, collection: proc{ [%(掲載中 掲載中), %w(掲載停止 掲載停止)] } # カスタムフィルターだと, as: を指定する必要がある. よって, 下では表示すらされない # filter :active_admin_custom_filter, label: "掲載(求) # override controller do def scoped_collection end_of_association_chain.join_jobs end end endgem ranscakerの不具合はこちらの記事を参照させていただきました.
画面とまとめ
これで親(Shop)にはない,子(Job)の要素(publishedカラム)でfilterができました.
詳細画面(Show)
if文の挙動
特定のカラムに値があったら, 表示する.
なければ表示しない. という単純な仕様app/admin/shops.rbActiveAdmin.register Shop do show do panel "求人(Job)情報" do attributes_table_for shop.job do #① idがあれば表示 if resource.job.job_img1_id.present? row :job_img1_id end #② idがなければ表示 if resource.job.job_img2_id.nil? row :job_img2_id end #③ idがあれば表示 (ここ挙動がおかしい!!バグ!!) if :job_img3_id.present? row :job_img3_id end #④ idがなければ表示 (ここ挙動がおかしい!!バグ!!) if :job_img4_id.nil? row :job_img4_id end end end end end今 job_img[1-4]_id は値がないnull(nil)です.
つまり、写真2と写真4だけが表示されているのが正しい挙動です.実際の画面を見ると
写真1 写真4 => 非表示
写真2 写真3 => 表示
(上下のメイン写真コメントと写真5はここでは無関係)ここで
写真3は写真1同様に非表示とならなければなりません.同様に
写真4は写真2同様に表示とならなければなりません.
ここでバグがありました.結論
:シンボル使用した時と, resouceから呼び出した時で, 挙動が正反対になっています.
正しい挙動はresouceを使用した時です.
ifの引数にシンボリックの使用は推奨されません.
- 投稿日:2020-02-20T10:58:23+09:00
#Rails で nil は禁止するが blank ( 未入力/から文字の入力 ) は許容するバリデーション / exclusion: { in: [nil] }
Model
class User < ApplicationRecord validates :name, exclusion: { in: [nil] } end動作イメージ
User.create!(name: nil) ActiveRecord::RecordInvalid: 名前 は予約されています User.create!(name: "") # 成功ガイド
https://railsguides.jp/active_record_validations.html
presence は?
空文字を禁止してしまう
2.9 presence
このヘルパーは、指定された属性が「空でない」ことを確認します。値がnilや空文字でない(つまり空欄でもなければホワイトスペースでもない)ことを確認するために、内部でblank?メソッドを使っています。class Person < ApplicationRecord validates :name, :login, :email, presence: true endallow_blank は?
値が未入力、空文字の時にバリデーションをスキップするためオプション指定であり、空文字を許容するわけではない
3.2 :allow_blank
:allow_blankオプションは:allow_nilオプションと似ています。このオプションを指定すると、属性の値がblank?に該当する場合(nilや空文字など)にバリデーションがパスします。class Topic < ApplicationRecord validates :title, length: { is: 5 }, allow_blank: true end Topic.create(title: "").valid? # => true Topic.create(title: nil).valid? # => trueOriginal by Github issue
- 投稿日:2020-02-20T08:14:50+09:00
RailsアプリをHerokuにデプロイしたらクラッシュしてたときの対処法
開発環境では問題なく動いていたアプリが、満を持してデプロイしたときにクラッシュしてたら精神的に結構きますよね
正直、辛い。
今回は、Rails5のアプリをHerokuにデプロイした際にクラッシュを起こしていたので、そのとき取った行動をメモとして残しておきます。
エラー画面
We're sorry, but something went wrong.
If you are the application owner check the logs for more information.日本語でおk(死語)
このエラー文自体を検索し、色んなエラー時対応の記事を読み込んでいくと、
何にしろログを見なっせ
(書いてある)ということなので、
$ heroku logs
を叩きました。こちらの記事はとても参考になりました。
Qiita/[Ruby on Rails Tutorial]Herokuにデプロイ後Application error[H10 (App crashed)]が発生した時の対処法
$ heroku logs 2020-02-19T13:24:10.854459+00:00 app[web.1]: /app/vendor/bundle/ruby/2.5.0/gems/puma-3.9.1/lib/puma/runner.rb:138:in `load_and_bind' 2020-02-19T13:24:10.854460+00:00 app[web.1]: /app/vendor/bundle/ruby/2.5.0/gems/puma-3.9.1/lib/puma/single.rb:87:in `run' 2020-02-19T13:24:10.854460+00:00 app[web.1]: /app/vendor/bundle/ruby/2.5.0/gems/puma-3.9.1/lib/puma/launcher.rb:174:in `run' 2020-02-19T13:24:10.854461+00:00 app[web.1]: /app/vendor/bundle/ruby/2.5.0/gems/puma-3.9.1/lib/puma/cli.rb:77:in `run' 2020-02-19T13:24:10.854461+00:00 app[web.1]: /app/vendor/bundle/ruby/2.5.0/gems/puma-3.9.1/bin/puma:10:in `<top (required)>' 2020-02-19T13:24:10.854462+00:00 app[web.1]: /app/vendor/bundle/ruby/2.5.0/bin/puma:23:in `load' 2020-02-19T13:24:10.854462+00:00 app[web.1]: /app/vendor/bundle/ruby/2.5.0/bin/puma:23:in `<top (required)>' 2020-02-19T13:24:10.941495+00:00 heroku[web.1]: State changed from starting to crashed 2020-02-19T13:24:10.946163+00:00 heroku[web.1]: State changed from crashed to starting 2020-02-19T13:24:10.919655+00:00 heroku[web.1]: Process exited with status 1 2020-02-19T13:24:14.912178+00:00 heroku[web.1]: Starting process with command `bundle exec puma -C config/puma.rb` 2020-02-19T13:24:17.577966+00:00 app[web.1]: Puma starting in single mode... 2020-02-19T13:24:17.587312+00:00 app[web.1]: * Version 3.9.1 (ruby 2.5.7-p206), codename: Private Caller 2020-02-19T13:24:17.587332+00:00 app[web.1]: * Min threads: 5, max threads: 5 2020-02-19T13:24:17.587335+00:00 app[web.1]: * Environment: production 2020-02-19T13:24:19.870761+00:00 app[web.1]: ! Unable to load application: LoadError: No such file to load -- URI.rb 2020-02-19T13:24:19.870872+00:00 app[web.1]: bundler: failed to load command: puma (/app/vendor/bundle/ruby/2.5.0/bin/puma) 2020-02-19T13:24:19.870924+00:00 app[web.1]: LoadError: No such file to load -- URI.rb 2020-02-19T13:24:19.870926+00:00 app[web.1]: /app/vendor/bundle/ruby/2.5.0/gems/activesupport-5.1.6/lib/active_support/dependencies.rb:292:in `require' 2020-02-19T13:24:19.870926+00:00 app[web.1]: /app/vendor/bundle/ruby/2.5.0/gems/activesupport-5.1.6/lib/active_support/dependencies.rb:292:in `block in require' 2020-02-19T13:24:19.870927+00:00 app[web.1]: /app/vendor/bundle/ruby/2.5.0/gems/activesupport-5.1.6/lib/active_support/dependencies.rb:258:in `load_dependency' 2020-02-19T13:24:19.870927+00:00 app[web.1]: /app/vendor/bundle/ruby/2.5.0/gems/activesupport-5.1.6/lib/active_support/dependencies.rb:292:in `require' 2020-02-19T13:24:19.870928+00:00 app[web.1]: /app/app/controllers/application_controller.rb:5:in `<class:ApplicationController>' 2020-02-19T13:24:19.870928+00:00 app[web.1]: /app/app/controllers/application_controller.rb:1:in `<top (required)>' 2020-02-19T13:24:19.870929+00:00 app[web.1]: /app/vendor/bundle/ruby/2.5.0/gems/activesupport-5.1.6/lib/active_support/dependencies.rb:292:in `require' 2020-02-19T13:24:19.870929+00:00 app[web.1]: /app/vendor/bundle/ruby/2.5.0/gems/activesupport-5.1.6/lib/active_support/dependencies.rb:292:in `block in require' 2020-02-19T13:24:19.870930+00:00 app[web.1]: /app/vendor/bundle/ruby/2.5.0/gems/activesupport-5.1.6/lib/active_support/dependencies.rb:258:in `load_dependency' 2020-02-19T13:24:19.870930+00:00 app[web.1]: /app/vendor/bundle/ruby/2.5.0/gems/activesupport-5.1.6/lib/active_support/dependencies.rb:292:in `require' 2020-02-19T13:24:19.870930+00:00 app[web.1]: /app/vendor/bundle/ruby/2.5.0/gems/activesupport-5.1.6/lib/active_support/dependencies.rb:379:in `block in require_or_load' 2020-02-19T13:24:19.870931+00:00 app[web.1]: /app/vendor/bundle/ruby/2.5.0/gems/activesupport-5.1.6/lib/active_support/dependencies.rb:36:in `block in load_interlock' 2020-02-19T13:24:19.870931+00:00 app[web.1]: /app/vendor/bundle/ruby/2.5.0/gems/activesupport-5.1.6/lib/active_support/dependencies/interlock.rb:12:in `block in loading' 2020-02-19T13:24:19.870932+00:00 app[web.1]: /app/vendor/bundle/ruby/2.5.0/gems/activesupport-5.1.6/lib/active_support/concurrency/share_lock.rb:149:in `exclusive' 2020-02-19T13:24:19.870932+00:00 app[web.1]: /app/vendor/bundle/ruby/2.5.0/gems/activesupport-5.1.6/lib/active_support/dependencies/interlock.rb:11:in `loading' 2020-02-19T13:24:19.870933+00:00 app[web.1]: /app/vendor/bundle/ruby/2.5.0/gems/activesupport-5.1.6/lib/active_support/dependencies.rb:36:in `load_interlock' 2020-02-19T13:24:19.870933+00:00 app[web.1]: /app/vendor/bundle/ruby/2.5.0/gems/activesupport-5.1.6/lib/active_support/dependencies.rb:357:in `require_or_load' 2020-02-19T13:24:19.870934+00:00 app[web.1]: /app/vendor/bundle/ruby/2.5.0/gems/activesupport-5.1.6/lib/active_support/dependencies.rb:335:in `depend_on' 2020-02-19T13:24:19.870934+00:00 app[web.1]: /app/vendor/bundle/ruby/2.5.0/gems/activesupport-5.1.6/lib/active_support/dependencies.rb:251:in `require_dependency' 2020-02-19T13:24:19.870934+00:00 app[web.1]: /app/vendor/bundle/ruby/2.5.0/gems/railties-5.1.6/lib/rails/engine.rb:476:in `block (2 levels) in eager_load!' 2020-02-19T13:24:19.870935+00:00 app[web.1]: /app/vendor/bundle/ruby/2.5.0/gems/railties-5.1.6/lib/rails/engine.rb:475:in `each' 2020-02-19T13:24:19.870935+00:00 app[web.1]: /app/vendor/bundle/ruby/2.5.0/gems/railties-5.1.6/lib/rails/engine.rb:475:in `block in eager_load!' 2020-02-19T13:24:19.870935+00:00 app[web.1]: /app/vendor/bundle/ruby/2.5.0/gems/railties-5.1.6/lib/rails/engine.rb:473:in `each' 2020-02-19T13:24:19.870936+00:00 app[web.1]: /app/vendor/bundle/ruby/2.5.0/gems/railties-5.1.6/lib/rails/engine.rb:473:in `eager_load!' 2020-02-19T13:24:19.870936+00:00 app[web.1]: /app/vendor/bundle/ruby/2.5.0/gems/railties-5.1.6/lib/rails/engine.rb:354:in `eager_load!' 2020-02-19T13:24:19.870937+00:00 app[web.1]: /app/vendor/bundle/ruby/2.5.0/gems/railties-5.1.6/lib/rails/application/finisher.rb:67:in `each' 2020-02-19T13:24:19.870937+00:00 app[web.1]: /app/vendor/bundle/ruby/2.5.0/gems/railties-5.1.6/lib/rails/application/finisher.rb:67:in `block in <module:Finisher>' 2020-02-19T13:24:19.870937+00:00 app[web.1]: /app/vendor/bundle/ruby/2.5.0/gems/railties-5.1.6/lib/rails/initializable.rb:30:in `instance_exec' 2020-02-19T13:24:19.870938+00:00 app[web.1]: /app/vendor/bundle/ruby/2.5.0/gems/railties-5.1.6/lib/rails/initializable.rb:30:in `run' 2020-02-19T13:24:19.870938+00:00 app[web.1]: /app/vendor/bundle/ruby/2.5.0/gems/railties-5.1.6/lib/rails/initializable.rb:59:in `block in run_initializers' 2020-02-19T13:24:19.870939+00:00 app[web.1]: /app/vendor/ruby-2.5.7/lib/ruby/2.5.0/tsort.rb:228:in `block in tsort_each' 2020-02-19T13:24:19.870939+00:00 app[web.1]: /app/vendor/ruby-2.5.7/lib/ruby/2.5.0/tsort.rb:350:in `block (2 levels) in each_strongly_connected_component' 2020-02-19T13:24:19.870939+00:00 app[web.1]: /app/vendor/ruby-2.5.7/lib/ruby/2.5.0/tsort.rb:431:in `each_strongly_connected_component_from' 2020-02-19T13:24:19.870940+00:00 app[web.1]: /app/vendor/ruby-2.5.7/lib/ruby/2.5.0/tsort.rb:349:in `block in each_strongly_connected_component' 2020-02-19T13:24:19.870940+00:00 app[web.1]: /app/vendor/ruby-2.5.7/lib/ruby/2.5.0/tsort.rb:347:in `each' 2020-02-19T13:24:19.870941+00:00 app[web.1]: /app/vendor/ruby-2.5.7/lib/ruby/2.5.0/tsort.rb:347:in `call' 2020-02-19T13:24:19.870941+00:00 app[web.1]: /app/vendor/ruby-2.5.7/lib/ruby/2.5.0/tsort.rb:347:in `each_strongly_connected_component' 2020-02-19T13:24:19.870942+00:00 app[web.1]: /app/vendor/ruby-2.5.7/lib/ruby/2.5.0/tsort.rb:226:in `tsort_each' 2020-02-19T13:24:19.870942+00:00 app[web.1]: /app/vendor/ruby-2.5.7/lib/ruby/2.5.0/tsort.rb:205:in `tsort_each' 2020-02-19T13:24:19.870947+00:00 app[web.1]: /app/vendor/bundle/ruby/2.5.0/gems/railties-5.1.6/lib/rails/initializable.rb:58:in `run_initializers' 2020-02-19T13:24:19.870947+00:00 app[web.1]: /app/vendor/bundle/ruby/2.5.0/gems/railties-5.1.6/lib/rails/application.rb:353:in `initialize!' 2020-02-19T13:24:19.870948+00:00 app[web.1]: /app/config/environment.rb:5:in `<top (required)>' 2020-02-19T13:24:19.870948+00:00 app[web.1]: config.ru:3:in `require_relative' 2020-02-19T13:24:19.870948+00:00 app[web.1]: config.ru:3:in `block in <main>' 2020-02-19T13:24:19.870949+00:00 app[web.1]: /app/vendor/bundle/ruby/2.5.0/gems/rack-2.2.2/lib/rack/builder.rb:116:in `eval' 2020-02-19T13:24:19.870949+00:00 app[web.1]: /app/vendor/bundle/ruby/2.5.0/gems/rack-2.2.2/lib/rack/builder.rb:116:in `new_from_string' 2020-02-19T13:24:19.870950+00:00 app[web.1]: /app/vendor/bundle/ruby/2.5.0/gems/rack-2.2.2/lib/rack/builder.rb:105:in `load_file' 2020-02-19T13:24:19.870950+00:00 app[web.1]: /app/vendor/bundle/ruby/2.5.0/gems/rack-2.2.2/lib/rack/builder.rb:66:in `parse_file' 2020-02-19T13:24:19.870951+00:00 app[web.1]: /app/vendor/bundle/ruby/2.5.0/gems/puma-3.9.1/lib/puma/configuration.rb:313:in `load_rackup' 2020-02-19T13:24:19.870951+00:00 app[web.1]: /app/vendor/bundle/ruby/2.5.0/gems/puma-3.9.1/lib/puma/configuration.rb:242:in `app' 2020-02-19T13:24:19.870951+00:00 app[web.1]: /app/vendor/bundle/ruby/2.5.0/gems/puma-3.9.1/lib/puma/runner.rb:138:in `load_and_bind' 2020-02-19T13:24:19.870952+00:00 app[web.1]: /app/vendor/bundle/ruby/2.5.0/gems/puma-3.9.1/lib/puma/single.rb:87:in `run' 2020-02-19T13:24:19.870952+00:00 app[web.1]: /app/vendor/bundle/ruby/2.5.0/gems/puma-3.9.1/lib/puma/launcher.rb:174:in `run' 2020-02-19T13:24:19.870952+00:00 app[web.1]: /app/vendor/bundle/ruby/2.5.0/gems/puma-3.9.1/lib/puma/cli.rb:77:in `run' 2020-02-19T13:24:19.870952+00:00 app[web.1]: /app/vendor/bundle/ruby/2.5.0/gems/puma-3.9.1/bin/puma:10:in `<top (required)>' 2020-02-19T13:24:19.870953+00:00 app[web.1]: /app/vendor/bundle/ruby/2.5.0/bin/puma:23:in `load' 2020-02-19T13:24:19.870953+00:00 app[web.1]: /app/vendor/bundle/ruby/2.5.0/bin/puma:23:in `<top (required)>' 2020-02-19T13:24:20.204439+00:00 heroku[web.1]: State changed from starting to crashed 2020-02-19T13:24:20.185683+00:00 heroku[web.1]: Process exited with status 1 2020-02-19T13:24:21.120419+00:00 heroku[router]: at=error code=H10 desc="App crashed" method=GET path="/" host=example.herokuapp.com request_id=eb4b6551-949a-4693-9f76-2b0bcd0e92dd fwd="219.100.188.52" dyno= connect= service= status=503 bytes= protocol=https心折れそう
折れてました。
辛かったので、エラー文の一番最後にあった
heroku[router]: at=error code=H10 desc="App crashed" method=GET path="/"
を、エラーコード記載されてるし後はグーグル先生がどうにかしてくれるだろうと脳死で検索しました。結果的に分かったのは、
エラー文見ろ
厳しい!!!
目から溢れる汁で滲む画面を読み進めたところ、次の部分に目が留まる(とはいっても序盤の方)。
2020-02-19T13:24:19.870761+00:00 app[web.1]: ! Unable to load application: LoadError: No such file to load -- URI.rb 2020-02-19T13:24:19.870872+00:00 app[web.1]: bundler: failed to load command: puma (/app/vendor/bundle/ruby/2.5.0/bin/puma) 2020-02-19T13:24:19.870924+00:00 app[web.1]: LoadError: No such file to load -- URI.rbご丁寧に
!
までつけてある。ここでピーンと来て、そういえばネットから次のコードをコピペしたときに、
example_controller.rbrequire 'net/https' require 'uri' # 'URI'と記載していたで実行したところ、
uriなんてねえよバーカ!
と怒られたので、
'uri'
=>'URI'
と訂正したことを思い出しました。お ま え か。
その後、正しく
require 'uri'
と訂正すると無事動いてくれました。エラー文を読もう(結論)
心折れかかったら息抜きも忘れないでください。
- 投稿日:2020-02-20T00:58:47+09:00
Ruby on Rails アソシエーションの応用 class_name 【一つのモデルを複数に分岐する】
忘れているかもしれないので、
まずは基本から復習していきましょう!!基本(1対多)
userが複数の商品(products)を保持している関係だとしたら、
user.rbclass User < ApplicationRecord has_many :products endproductsはuserに保持されているので、
product.rbclass Product < ApplicationRecord belongs_to :user enduserというフォルダに、複数のproductファイルがある感覚と同じ。
だからuserは単数形、productは複数形
productは親フォルダのuserに入っているから、belongs_to :user
userフォルダはたくさんのproductを持っているから、 has_many :products応用(1対多): class_name
じゃあ、productsを保持するuserが、販売者(seller)・購入者(buyer)に分ける場合はどうしましょうか?
seller・buyerにそれぞれモデルは必要ありません。userモデルを分けてあげれば大丈夫です。じゃないとIDが変わって逆に大変ですから。userはproductsを複数保有しているので同じです。
(次にやります。)user.rbclass User < ApplicationRecord has_many :products endproductsに紐づくuserを"seller"と"buyer"に分けたいので、下記ようにします。
product.rbclass Product < ApplicationRecord belongs_to :seller, class_name: "User", foreign_key: "seller_id" belongs_to :buyer, class_name: "User", foreign_key: "buyer_id" end「なんじゃこりゃ!!」、となったと思います。要点を抑えたら簡単です。
では解説していきます。STEP1 モデル名は変えられる。
基礎だと下記のように、ProductはUserに紐づいていました。
product.rbclass Product < ApplicationRecord belongs_to :user endbelongs_to :userと記述されていますが、この"user"という名前を変えられます。
「userじゃなくて、ownerに変えたいなあ??」
このように名前を変えたい場合にclass_nameを使います。product.rbclass Product < ApplicationRecord belongs_to :owner, class_name: "User" # belongs_to :userと同じです。 # 元はUserモデルだけど(class_name: "User")、ownerって表示に変えますわ(belongs_to :owner) endSTEP2: Userモデルを分ける
1つだとSTEP1だけで変えられます(まあ、変える必要ないんだけど)。
では、userをsellerとbuyerと2つに分けたい場合、どうすればいいのでしょうか?まずはproductsテーブルをみてください。
productレコードのカラムに"buyer_id(user_id)"と"seller_id(user_id)"があります
このカラムに入れるidを元にuserをsellerとbuyerに分けています。product.rbclass Product < ApplicationRecord belongs_to :seller, class_name: "User", foreign_key: "seller_id" # belongs_to :seller, class_name: "User" = (Productに紐づく) Userモデルをsellerと定義するわ。 # foreign_key: "seller_id" = user_idはProductレコードの『seller_idカラム』のid番号を使うわ belongs_to :buyer, class_name: "User", foreign_key: "buyer_id" # belongs_to :buyer, class_name: "User" = (Productに紐づく) Userモデルをbuyerと定義するわ。 # foreign_key: "seller_id" = user_idはProductレコードの『buyer_idカラム』のid番号を使うわ endこれで二つに分けられました。
カラムに紐づくid番号で分けたあげた分けです。STEP3: Productを複数に分ける
販売中、売却済み、購入した商品に分けます。
user.rbclass User < ApplicationRecord # 全部取得 has_many :products # 販売中 has_many :selling_products, class_name: "Product", foreign_key: "seller_id", -> { where("buyer_id is NULL") } # Productモデル名を selling_productsに定義するわ = has_many :selling_products, class_name: "Product" # productレコードの”seller_id”カラムに自分のidが入っている、productだけ取得するね = foreign_key: "seller_id" ( userはhas_many: productsだから ) # でも、buyer_idが入っていないproductだけにする = -> { where("buyer_id is NULL") } # 買った商品 has_many :bought_products, class_name: "Product", foreign_key: "buyer_id" # Productモデル名を bought_productsに定義するわ = has_many :bought_products, class_name: "Product" # productレコードの”buyer_id”カラムに自分のidが入っている、productだけ取得するね = foreign_key: "buyer_id" ( userはhas_many: productsだから ) # 売却済み商品 has_many :sold_products, class_name: "Product", foreign_key: "seller_id", -> { where("buyer_id is not NULL") } # Productモデル名を sold_productsに定義するわ = has_many :selling_products, class_name: "Product" # productレコードの”seller_id”カラムに自分のidが入っている、productだけ取得するね = foreign_key: "seller_id" ( userはhas_many: productsだから ) # でも、buyer_idが入ってるproductだけにする = -> { where("buyer_id is not NULL") } endまとめ
class_nameでモデルを分ける際は、
まとめhas_many :〜側のモデルのカラムを使って分ける。 # 今回だと has_many :products # productにuser_idを紐づけるので、これを使って分ける。 # 親元のuserのカラムにproduct_idは入れないので、userのカラムを使って分けることはできない。 まとめ: 1対多なら、 『多』のカラムを使って分けよう ( foreign_key: )参考リンク
- 投稿日:2020-02-20T00:47:39+09:00
Railsでブログアプリケーションの作成時に、postを作成するだけでからのコメントが表示されるのを解消する。
発生した問題
タイトルにもある通り、railsで定番のブログのようなアプリを作っているときに、投稿を作成しそれの詳細をクリックしたらみられるようにshow.html.erbに記述。comment機能も追加した。しかし、投稿を作成するだけで、空のコメントが表示され、削除しようとするとRouting errorになる。
解決策
投稿詳細ページ(筆者はshow.html.erb)で、以下のように表示していた。
show.html.erb<div class="p-comment__list mx-auto"> <div class="p-comment__listTitle">コメント</div> <%= render @post.comments %> </div>それを、
show.html.erb<div class="p-comment__list mx-auto"> <div class="p-comment__listTitle">コメント</div> <%= render @post.comments.select(&:persisted?) %> </div>と変更した。
違いに気づいていただけただろうか。@post.commentsを@post.comments.select(&:persisted?)に変更した。直った!!!!!!!
参考URL
https://stackoverflow.com/questions/36680890/rails-empty-comment-on-every-new-post
- 投稿日:2020-02-20T00:29:51+09:00
[Rails]モデル削除コマンド
はじめに
たまに使おうとすると、ど忘れしてしまうため、備忘録として残しておきます。
最初にコマンドの形式を記載し、その後にサンプルとして適当なモデルの作成/削除を行ってみます。コマンドの形式
コマンドの形式$ rails destroy model [モデル名]サンプル
モデル作成
まず、以下のコマンドでUserモデルを作成。カラムは適当にnameだけにしておきます。
モデル作成コマンド$ rails g model User name:string invoke active_record create db/migrate/20200219151817_create_users.rb create app/models/user.rb invoke test_unit create test/models/user_test.rb create test/fixtures/users.ymlモデル削除
以下のコマンドでUserモデルを削除する。
モデル作成コマンド$ rails destroy model user invoke active_record remove db/migrate/20200219151817_create_users.rb remove app/models/user.rb invoke test_unit remove test/models/user_test.rb remove test/fixtures/users.yml注意点
上記のコマンドでは、create以外のmigrationファイルを削除してくれません。
サンプルで言うと、User作成後にageを追加した場合、このmigrationファイルは残ったままになります。
なので、残ったmigrationファイルは手動で削除してください。