20200220のRailsに関する記事は26件です。

複数テーブルに存在するカラムのi18n定義を共通化する

複数テーブルに共通して存在するカラム(ex. created_at, updated_at)を各テーブルで定義するのは冗長でめんどくさい。

config/locales/translation_ja.yml
ja:
  activerecord:
    attributes:
      user:
        name: ユーザ名
        created_at: 登録日時
        updated_at: 更新日時
      event:
        name: イベント名
        created_at: 登録日時
        updated_at: 更新日時

こんな風に書かなくても、attributes直下で定義すれば使い回すことができる。

config/locales/translation_ja.yml
ja:
  attributes:
    created_at: 登録日時
    updated_at: 更新日時
<%= l(User.created_at) %>

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

deviseにユーザー詳細や編集機能を付ける

はじめに

deviseを使ってログイン機能を作った後にユーザー情報のページを実装したいと思い作ってみたら変なところで詰まったので書いておくことにしました。(備忘録)

前提

gem 'devise' でログイン機能実装済み(email,password)

ユーザーの一覧ページを作る

$ rails g controller users index
routes.rb
devise_for :users
resources :users

resourcesをdivise_forの下に書く。上に書くとdeviceが優先されてログイン、ログアウトの時にidが変なところに入ってバグる。

indexページのルーティングとビューは各々好きに作る。

カラムの追加

$ add_name_to_users name:string
$ rails db:migrate

この呪文をいつも忘れてググってるのでここに書いておくことにしました。名前以外にも色々追加する。

機能実装

詳細、編集、削除を実装して完成。

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

Railsチュートリアルの開発環境を Docker でもっと便利になイカ!?

【概要】

本記事では、Gem の永続化、データベース及びテスト用メールサーバをコンテナ化していきます。
Railsチュートリアルの開発環境を Docker にしてみなイカ?の続きです。

さて、前回の記事では、Railsチュートリアルの Sample App をコンテナ化しました。
しかし、実際の開発にあたり、煩わしい部分や便利になるところがありますので、改善していきましょう。



【本文】

□ 事前準備

  • 前回の記事と同じファイル及びディレクトリ構成です。
docker/Dockerfile
FROM 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.yml
version: '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.yml
version: '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.yml
version: '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.yml
default: &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

□ メールサーバのコンテナ化

■ 現状の問題点

■ 解決策

○ 1: docker-compose.yml の編集

docker-compose.yml
version: '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

□ 特記事項

■ 注意事項

  • 今のところ特になし

■ 使用イメージ、ライブラリ

□ おまけ: 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.yml
default: &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'
...

  1. docker-compose stopstartなら作業状態を継続できることから、Gem もインストール済で作業を継続できる。 

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

Railsで投稿した内容をGoogleMap上にmarkerで表示させる方法

はじめに

世界中の絶景を投稿できるアプリが作れます。
マップの表示方法はGoogleMapsAPIを使いました。
投稿した内容から自動的に軽度と緯度を取得してGoogleMapsAPIのマップ上に表示させます。
完成形はこんな感じ
スクリーンショット 2020-02-20 19.32.20.png

環境

  • Ruby 2.5.1
  • Rails 5.2.3
  • MySQL 5.6

目次

  • GoogleMapsAPIのKEYを取得
  • 簡単なアプリを作成しとりあえずGoogleMapsをアプリ上に表示させる
  • 投稿機能を作り軽度、緯度取得し、マップ上に表示

GoogleMapsのKEYを取得

(グーグルアカウントにログインしてから始めてください)
まずGoogle Map PlatformからKEYを取得します
スクリーンショット 2020-02-20 15.25.11.png

使ってみるを選択。
スクリーンショット 2020-02-20 15.28.16.png

全て選択し続行。
プロジェクト名はなんでもいいですが今回はMyProjectを選択
支払い方法を入力します。
topページに移動しますので下の方のMaps Platfomを選択
スクリーンショット 2020-02-20 15.40.51.png

Maps JavaScript APIを選択し有効を押します

スクリーンショット 2020-02-20 15.42.15.png

スクリーンショット 2020-02-20 15.47.16.png

画面上にあるAPI とサービスの認証情報

スクリーンショット 2020-02-20 15.48.21.png

認証情報を作成を選択しAPIを作成。
画面にでてくるのがAPIキーになるので保存しといてください。(必要な場合はキーの制限をしてください)

簡単なアプリを作成しgooglemapをアプリ上に表示させる

アプリ名はここではgmapにします
ここの部分は自分のアプリ名にしてください。
まずアプリの雛形とデータベース設計します

ターミナル上で

rails _5.2.3_ new gmap -d mysql
cd gmap
rails g controller maps
rails db:create
rails db:migrate

erbになっているのでhamlに変換します。
ここは任意で大丈夫です。
erbをhamlに簡単に変えてくれるサイトです

Gemfile
gem "haml-rails", ">= 1.0", '<= 2.0.1'
bundel install
rails haml:erb2haml

マップを表示させます
rails5.2以上なので今回はcredentialsを使います

routes.rb
Rails.application.routes.draw do
  root to: "maps#index"
end
views/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:edit
GOOGLE_MAP_KEY: MyAPIKey

MyAPIKeyの部分は先ほど取得したkeyを入力してください
これで一応マップ表示ができたと思います!
スクリーンショット 2020-02-20 18.00.34.png

投稿機能を作り軽度、緯度取得

まずは投稿機能を作るためにpostsのviewとcontroller,modelを作ります。

rails g scaffold posts name:string description:string latitude:float longitude:float
rake db:migrate

表示をgmaps4railsのgemを使って表示する方法に変えます。

Gemfile
gem '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.rb
class 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
end

marker.infowindow post.descriptionのところに表示させたいカラムを変えれば表示内容を変えれます。
今回はdescriptionにします。

これでマーカーが立つようになりました。
あとは新規投稿で軽度、緯度を取得できるようにします

gem 'geocoder'
post.rb
class 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の項目を消します。
これで完成です!

最後に

間違っているところがあったら教えてください…

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

RailsアプリをDockerで作ってCircleCIで自動テストしてHerokuにデプロイした話

はじめに

どうも、Pirikaraです。
久しぶりの投稿となりました。

今回は、Docker環境でRailsアプリケーションを開発し、
CircleCIで自動テスト、Herokuにデプロイするところまでやっていきたいと思います。
個人開発でやってみましたが、このエラーの山々......
スクリーンショット 2020-02-20 15.00.43.png

一つ一つエラーを解決して設定ファイルを修正してを繰り返し繰り返し......
やっとまともに動くようになりました。
スクリーンショット 2020-02-20 15.03.12.png

初めてやるよって方は僕の屍を踏み越えていってください。
ちなみに間違ってるよーとか改善点とかご指摘いただけると幸いです。

環境

・Mac OS
・Ruby 2.5.3
・Rails 5.2.2
・MySQL 5.7

また、Dockerがインストールされていることを前提としています(Dockerコマンドが使用できる状態)。
インストールしていない場合は、公式サイトからアカウントを作ってログインし、DockerHubからダウンロード・インストールします。
Dockerhub

Herokuについても登録していない場合は登録の必要があります。
公式サイトから登録が可能です。
Heroku

アプリの構成

スクリーンショット 2020-02-20 13.02.15.png

開発環境から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

今ディレクトリはこんな感じです。
スクリーンショット 2020-02-20 14.16.06.png

では、それぞれ中身を書いていきましょう。(Gemfile.lockは空のままです)

Dockerfile
FROM 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_ROOT
docker-compose.yml
version: '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:
      - db
Gemfile
source '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.yml
default: &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へアクセスしてサーバーの起動を確認することができます。おめでとう。
スクリーンショット 2020-02-20 14.42.11.png

2.CircleCIの導入と自動テスト

こちらのブログをパク・・・参考にしました。
導入まで大変わかりやすく、参考になりました。こちらを参考に導入してみてください。

ウェブ系ウシジマくんのテックブログ

「.circleci」というディレクトリを作成し、これ以下に「config.yml」というファイルを作成します。
また、configディレクトリ以下に「database.yml.ci」というファイルを作成します。

こんな感じ。
スクリーンショット 2020-02-20 14.56.46.png

では、作成したファイルの中身を書いていきましょう。

config.yml
version: 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-results
database.yml.ci
test:
  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することができません。
スクリーンショット 2020-02-20 19.50.23.png
ほらね。

Detailのリンクからエラー箇所も教えて頂けます。
スクリーンショット 2020-02-20 19.54.06.png

テストをパスすると、Mergeボタンがアクティブになります。
スクリーンショット 2020-02-20 20.00.18.png

※ seeds.rbを読み込みたい場合

.circleci/config.ymlでseeds.rbの読み込み設定をしたところ、うまくいかなかったのでspec_helperにseeds.rbを読み込む設定をしました。

「database_cleaner」というgemを導入し、spec_helperへ処理を書いていきます。
これで、Rspec実行の際にデータベースがリフレッシュされ、seeds.fileが読み込まれます。

Githubへpushした際に実行された場合は大丈夫なのですが、ローカルでdocker-composeコマンドからrspecを実行した場合にデータベースがリセットされてしまうので注意してください。

Gemfile
group :development, :test do
  # rspec実行時にDBをリセットする
  gem 'database_cleaner'
end
spec_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
end

3.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.yml
production:
  <<: *default
  adapter: postgresql
  encoding: unicode
  pool: 5

そして、「heroku.yml」というファイルを新たに作成します。
作成場所はアプリ直下です。(GemfileとかDockerfileと一緒)
今回はDockerコンテナを使用して開発をしているので、コンテナごとHerokuのサーバーに持っていきます。
そのための設定ファイルです。知らんけど。

DockerコンテナをHerokuにアップする方法は公式サイトを参考にしました。

Dockerを使用したデプロイ

heroku.yml
build:
  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を......
スクリーンショット 2020-02-20 18.47.58.png

アプリケーションの設定からHEROKU_APP_NAMEを......
スクリーンショット 2020-02-20 19.28.44.png
それぞれ取得します。

CircleCIのアプリケーション管理画面から、右上の「Project Setting」をクリックします。
スクリーンショット 2020-02-20 19.30.15.png

Environment Variablesから「Add Variables」をクリックして、先ほど取得した値を環境変数として設定します。
これがconfig.ymlで使用されます。
スクリーンショット 2020-02-20 19.33.39.png

設定は以上です。
これで、

Githubへpush → CircleCiが自動でテスト → パスすればHerokuに自動でデプロイ

という風に動きます。良かったね。

終わりに

DockerとCircleCIを利用したのは初めてでしたが、
環境構築もテスト・デプロイも簡潔になったので、非常に便利でした。

特にテストコマンドやデプロイコマンドについては「コマンド打つだけやしなぁ......」と自動化の恩恵を甘く見ていましたが、
チリも積もればなんとやらです。
いちいちコマンドを打つ煩わしさから解放され、アプリの開発スピードも段違いだったように感じます。

参考にしていただけたら幸いです。

またね。

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

【初心者向け】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.html

Webcamp様にも記載ございました
https://web-camp.io/magazine/archives/17665

何をすればいいのか?

Gemfileの中にあるRailsのgemのバージョンをアップグレードする

Railsバージョンアップデートの手順

  1. gemファイルのrailsを5.1以上で指定する(ここでは5.2.4を指定しています)
gem 'rails', '~> 5.2.4'
  1. bundle updateする(※注意:bundle installではない!!!!)
bundle update

Railsをバージョンアップした際に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

もし
ここは違う、ここはこうした方が良い等々ございましたらご指摘いただけますと幸いです。
最後までみていただき、ありがとうございます。

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

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を一旦取得し記述するとエラーは解決。

しかしこのままではセキュリティに問題がある為、別の方法を模索中です。

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

処理速度が早い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になる。

これだと一度しか部分テンプレートが読み込まれないから、処理速度が早まる。

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

rubocop-railsのfind_by_*系メソッドの自動修正をstaticに定義した一部のメソッドの上で無効にする

Railsで開発するにあたって、rubocop-railsをLinterとして利用しており、
自動修正機能を適用したときにエラーに繋ってしまったのでメモしておきます。

背景

Railsではfind_by_idfind_by_nameのように、ActiveModelのカラム名を利用して検索できるメソッドが動的に定義されます。

しかし、このような個別のメソッドを利用するのではなくfind_by形式に統一するスタイルがRuboCopにおいては推奨されており、検知&自動修正するためのCopが存在します

一方で、自前でfind_by_hogeのようなメソッドを定義した場合にもこのCopの判定対象になってしまい、自動修正の対象になってしまいます。

自分の開発しているプロジェクトでは自動修正をかけた際に意図せず変更されてしまい、メソッド未定義のエラーに繋りました。

回避方法

一部のメソッドをこのCopの対象から除外したい場合には以下のアプローチがあります:

  • 該当行にコメントで rubocop:disable Rails/DynamicFindBy を追加する
  • rubocop.yml中で該当メソッド名をホワイトリストに追加する

2つの方法があるなかで、私は後者の選択をしました。

判断の理由としては、前者のインラインコメントで都度対応をするという方法は

  • 実装者がコメントの追加を忘れやすい(コードレビューでも指摘忘れやすい)
  • 後からコードを読むメンバーにコメントの背景が伝わりづらい
  • 呼びだしの箇所で個別に記述するコストがかかる

といったいくつかのデメリットがあると考えたためです。

そのため、後者の.rubocop.yml 中に記述するアプローチを取りました。

rubocop.yml
Rails/DynamicFindBy:
  Whitelist:
    - find_by_sql
    - find_by_hoge

Whitelistに該当メソッドだけでなく、find_by_sqlについても追加していますが、こちらのメソッドもRailsの標準のメソッドであり自動修正の対象とはしたくないためです。

RuboCopの設定は、デフォルトの設定を自動的に継承するようになっていますが、

継承されている設定と同じ項目についての設定を記述した時に以下のようなルールで上書きがされます。

  • ハッシュマップはデフォルトの物とマージされ、衝突したキーについては最後の設定で上書きされる
  • 配列については常に最後の設定によって上書きされる

(参考: Configuration - RuboCop: The Ruby Linter that Serves and Protects)

今回は後者の配列の上書きに該当しますので、標準でWhitelistに追加されているfind_by_sqlについても自分で追加しておく必要がありました。

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

えっ、環境変数ってコマンドの後ろに書いたらダメなんですか?

勘違いしていたこと

普段 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.rdoc

rake のおかげだったんだ・・・

まとめ

  • rake タスクを実行するときは、環境変数をコマンドの前に書いても後ろに書いても良い
  • それ以外の場合はコマンドの前に書く

使い分けるのも面倒なので、いっそのこと、常に前に書くようにしちゃってもいいのかなと思ってます。

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

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_racer

stackoverflowではmini_racerをインストールするように書かれていたものもありました。自分はこれでは解決しませんでした。

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

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にトライすると、マイグレートされました。

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

新規プロジェクトを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記述が、なぜファイルの中身を生成しなかったのか、まだわかってないので調べます!
分かる方いましたら教えてください!

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

あっさり読む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);
    })
    }
    `
  });
});
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

#Rails の ActiveRecord で DBの値が nil / NULL かどうかを モデルのインスタンスに問うメソッド

Uder#name が nil かどうかを聞くには User#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/2995

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

#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
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

#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: "")
# 成功

ガイド

image

https://railsguides.jp/active_record_validations.html

presence は?

空文字を禁止してしまう

2.9 presence
このヘルパーは、指定された属性が「空でない」ことを確認します。値がnilや空文字でない(つまり空欄でもなければホワイトスペースでもない)ことを確認するために、内部でblank?メソッドを使っています。

class Person < ApplicationRecord
  validates :name, :login, :email, presence: true
end

allow_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? # => true

Original by Github issue

https://github.com/YumaInaura/YumaInaura/issues/2994

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

[Ruby] irb や pry で例外時のバックトレースを表示する

TL;DR

irb や pry で例外時のバックトレースを表示するには

  • $@ を参照する。
  • wtf コマンドを使う (pry 限定) 。
    • ちなみに、現実世界の会話では wtf (What the Fxck!) なんてあまり使わないように ?‍♀️

バージョン情報

$ ruby -v
ruby 2.6.5p114 (2019-10-01 revision 67812) [x86_64-darwin18]

$ bin/rails -v
Rails 6.0.1  

実例

以下のモデルを作成した。

app/models/hunter.rb
class 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.rb
class 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 しておく。

Gemfile
group :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>'

参考

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

[Ruby][初心者向け] コンソールで例外が発生したときは、バックトレースを表示して原因を調べよう

typo など特に凡ミス対策に有効です ?

TL;DR

  • コンソール (irb や pry) で例外が発生しても、バックトレースが表示されないので原因がわからないことがある。
  • コンソールで例外時のバックトレースを表示するには
    • 特殊変数 $@ を参照する。
    • wtf コマンドを使う (pry 限定) 。
      • ちなみに、現実世界の会話では wtf (What the Fxck!) なんてあまり使わないように ?‍♀️

バージョン情報

$ ruby -v
ruby 2.6.5p114 (2019-10-01 revision 67812) [x86_64-darwin18]

$ bin/rails -v
Rails 6.0.1  

実例

以下のモデルを作成した。

app/models/hunter.rb
class 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.rb
class 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 しておく。

Gemfile
group :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>'

参考

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

ActiveAdminのTips

最近ActiveAdminを使用しているのですが
便利な反面, 仕様がわかりづらく, バグでは?と思うことが多いです.
私が躓いたところをいろいろ追加して行こうと思います.

前提環境

activeadmin (1.4.3)

詳細画面

if文の挙動

特定のカラムに値があったら, 表示する.
なければ表示しない. という単純な仕様

shops.rb
ActiveAdmin.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だけが表示されているのが正しい挙動です.

実際の画面を見ると

スクリーンショット 2020-02-20 10.53.44.png
写真1 写真4 => 非表示
写真2 写真3 => 表示
(上下のメイン写真コメントと写真5はここでは無関係)

ここで
写真3は写真1同様に非表示とならなければなりません.同様に
写真4は写真2同様に表示とならなければなりません.
ここでバグがありました.

結論

:シンボル使用した時と, resouceから呼び出した時で, 挙動が正反対になっています.
正しい挙動はresouceを使用した時です.
ifの引数にシンボリックの使用は推奨されません.

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

ActiveAdminのTips& Bugs

最近ActiveAdminを使用しているのですが
便利な反面, 仕様がわかりづらく, バグでは?と思うことが多いです.
私が躓いたところをいろいろ追加して行こうと思います.
日本語の記事が見つからなかったので, 書きました.

前提環境

activeadmin (1.4.3)

一覧画面(Index)

カスタムフィルター(子テーブルの要素をFilter対象とする)

前提

親(Shop) : 子(Job) = 1 : N 関係の時,
親要素に子要素をJOINして, DBのビューのような使い方をしている.

実現したいこと

子(Job)の特定のカラム(ここでは:published)の値でフィルター機能を作りたい

ソースコード

app/models/shop.rb
class 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]
  end
app/admin/shops.rb
ActiveAdmin.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
end

gem ranscakerの不具合はこちらの記事を参照させていただきました.

参照1
参照2

画面とまとめ

スクリーンショット 2020-02-20 16.50.57.png

これで親(Shop)にはない,子(Job)の要素(publishedカラム)でfilterができました.

参照

詳細画面(Show)

if文の挙動

特定のカラムに値があったら, 表示する.
なければ表示しない. という単純な仕様

app/admin/shops.rb
ActiveAdmin.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だけが表示されているのが正しい挙動です.

実際の画面を見ると

スクリーンショット 2020-02-20 10.53.44.png
写真1 写真4 => 非表示
写真2 写真3 => 表示
(上下のメイン写真コメントと写真5はここでは無関係)

ここで
写真3は写真1同様に非表示とならなければなりません.同様に
写真4は写真2同様に表示とならなければなりません.
ここでバグがありました.

結論

:シンボル使用した時と, resouceから呼び出した時で, 挙動が正反対になっています.
正しい挙動はresouceを使用した時です.
ifの引数にシンボリックの使用は推奨されません.

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

#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: "")
# 成功

ガイド

image

https://railsguides.jp/active_record_validations.html

presence は?

空文字を禁止してしまう

2.9 presence
このヘルパーは、指定された属性が「空でない」ことを確認します。値がnilや空文字でない(つまり空欄でもなければホワイトスペースでもない)ことを確認するために、内部でblank?メソッドを使っています。

class Person < ApplicationRecord
  validates :name, :login, :email, presence: true
end

allow_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? # => true

Original by Github issue

https://github.com/YumaInaura/YumaInaura/issues/2993

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

RailsアプリをHerokuにデプロイしたらクラッシュしてたときの対処法

開発環境では問題なく動いていたアプリが、満を持してデプロイしたときにクラッシュしてたら精神的に結構きますよね

正直、辛い。

今回は、Rails5のアプリをHerokuにデプロイした際にクラッシュを起こしていたので、そのとき取った行動をメモとして残しておきます。

エラー画面

image.png

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.rb
require 'net/https'
require 'uri'        # 'URI'と記載していた

で実行したところ、uriなんてねえよバーカ!と怒られたので、
'uri'=>'URI'と訂正したことを思い出しました。

お ま え か。

その後、正しくrequire 'uri'と訂正すると無事動いてくれました。

エラー文を読もう(結論)

心折れかかったら息抜きも忘れないでください。

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

Ruby on Rails アソシエーションの応用 class_name 【一つのモデルを複数に分岐する】

忘れているかもしれないので、
まずは基本から復習していきましょう!!

基本(1対多)

userが複数の商品(products)を保持している関係だとしたら、

user.rb
class User < ApplicationRecord
  has_many :products
end

productsはuserに保持されているので、

product.rb
class Product < ApplicationRecord
  belongs_to :user
end

userというフォルダに、複数のproductファイルがある感覚と同じ。
スクリーンショット 2020-02-20 0.21.45.png

だから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.rb
class User < ApplicationRecord
  has_many :products
end

productsに紐づくuserを"seller"と"buyer"に分けたいので、下記ようにします。

product.rb
class 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.rb
class Product < ApplicationRecord
  belongs_to :user
end

belongs_to :userと記述されていますが、この"user"という名前を変えられます。
「userじゃなくて、ownerに変えたいなあ??」
このように名前を変えたい場合にclass_nameを使います。

product.rb
class Product < ApplicationRecord
  belongs_to :owner, class_name: "User" # belongs_to :userと同じです。
  #  元はUserモデルだけど(class_name: "User")、ownerって表示に変えますわ(belongs_to :owner)
end

STEP2: Userモデルを分ける

1つだとSTEP1だけで変えられます(まあ、変える必要ないんだけど)。
では、userをsellerとbuyerと2つに分けたい場合、どうすればいいのでしょうか?

まずはproductsテーブルをみてください。
スクリーンショット 2020-02-20 0.33.06.png
productレコードのカラムに"buyer_id(user_id)"と"seller_id(user_id)"があります
このカラムに入れるidを元にuserをsellerとbuyerに分けています。

product.rb
class 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を複数に分ける

販売中、売却済み、購入した商品に分けます。

販売中だけ習得する
スクリーンショット 2020-02-20 1.47.52.png

売却済みだけ取得する
スクリーンショット 2020-02-20 1.49.58.png

購入した商品だけ取得する
スクリーンショット 2020-02-20 1.51.41.png

user.rb
class 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: )

参考リンク

railsアソシエーションオプションのメモ
アソシエーションにおけるclass_nameの定義!

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

Railsでブログアプリケーションの作成時に、postを作成するだけでからのコメントが表示されるのを解消する。

発生した問題

タイトルにもある通り、railsで定番のブログのようなアプリを作っているときに、投稿を作成しそれの詳細をクリックしたらみられるようにshow.html.erbに記述。comment機能も追加した。しかし、投稿を作成するだけで、空のコメントが表示され、削除しようとするとRouting errorになる。

スクリーンショット 2020-02-20 0.43.27.png

解決策

投稿詳細ページ(筆者は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?)に変更した。

スクリーンショット 2020-02-20 0.48.06.png

直った!!!!!!!

参考URL

https://stackoverflow.com/questions/36680890/rails-empty-comment-on-every-new-post

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

[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ファイルは手動で削除してください。

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