20210121のRailsに関する記事は30件です。

Railsのルーティングについて学び直そうと思う…①

はじめに

学習初期から、ルーティングについては、resourcesを使うことが多かったので、よく理解できていない部分が多々あったので、少しずつ調べて紹介していこうと思います。

リソースを同時に定義する

resources :users
resources :admins
resources :users, :admins

上の2つは全く同じです。
resourcesを全て使うなら、まとめた方が読みやすいですね。

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

【Rails Tutorial】第1章 ゼロからデプロイまで

はじめに

Railsチュートリアルをはじめるにあたり、演習の回答やエラーに対する解決方法、解決に至った経緯などを記録しています。

演習

1.3.2 rails server

1.3.2 - 1

デフォルトのRailsページに表示されているものと比べて、今の自分のコンピュータにあるRubyのバージョンはいくつになっていますか? コマンドラインでruby -vを実行することで簡単に確認できます。

デフォルトのRailsページ
Ruby version: ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-linux]
自分のコンピュータにあるバージョン
ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-linux]

1.3.2 - 2

同様にして、Railsのバージョンも調べてみましょう。調べたバージョンはリスト 1.2でインストールしたバージョンと一致しているでしょうか?

デフォルトのRailsページ
Rails version: 6.0.3
自分のコンピュータにあるバージョン
Rails 6.0.3

1.3.4 Hello, world!

1.3.4 - 1

リスト 1.9のhelloアクションを書き換え、「hello, world!」の代わりに「hola, mundo!」と表示されるようにしてみましょう。

app/controllers/application_controller.rb
class ApplicationController < ActionController::Base

  def hello
    render html: "hola, mundo!"
  end

end

1.3.4 - 2

Railsでは「非ASCII文字」もサポートされています。「¡Hola, mundo!」にはスペイン語特有の逆さ感嘆符「¡」が含まれています(図 1.23)17 。「¡」文字をMacで表示するには、Optionキーを押しながら1キーを押します。この文字をコピーして自分のエディタに貼り付ける方が早いかもしれません。

app/controllers/application_controller.rb
class ApplicationController < ActionController::Base

  def hello
    render html: "hola, mundo¡"
  end

end

1.3.4 - 3

リスト 1.9のhelloアクションを参考にして、2つ目のアクションgoodbyeを追加しましょう。このアクションは、「goodbye, world!」というテキストを表示します。リスト 1.11のルーティングを編集して、ルートルーティングの割り当て先をhelloアクションからgoodbyeアクションに変更します(図 1.24)。

app/controllers/application_controller.rb
class ApplicationController < ActionController::Base

  def hello
    render html: "hola, mundo¡"
  end

  def goodbye
    render html: "goodbye, world!"
  end

end
config/routes.rb
Rails.application.routes.draw do
  root 'application#goodbye'
end

1.5.3 Herokuにデプロイする(2)

1.5.3 - 1

1.3.4.1と同じ変更を行い、本番アプリでも「hola, mundo!」を表示できるようにしてください。

確認のみなので省略

1.5.3 - 2

1.3.4.1と同様、ルートへのルーティングを変更してgoodbyeアクションの結果が表示されるようにしてください。またデプロイ時には、Git pushのmasterをあえて省略し、git push herokuでデプロイできることを確認してみてください。

確認のみなので省略

1.5.4 Herokuコマンド

1.5.4 - 1

heroku helpコマンドを実行し、Herokuコマンドの一覧を表示してみてください。Herokuアプリのログを表示するコマンドはどれですか?

logs

1.5.4 - 2

上の演習で見つけたコマンドを使って、Herokuアプリの最近のログ(log)を調べてみましょう。直近に発生したイベントは何でしたか?(このログを調べるコマンドを覚えておくと、本番環境の不具合を見つけるときに役立ちます)

heroku logs


ちょっと小話

  • 単語を選択するとき、ドラッグしなくても、単語をダブルクリックすると検索できる。
  • Cloud9で行を下にコピーして、その行に移動したいときはcommand+option+down。 複数行を選択もできる。

GitHubにpushするときにエラーが出た

ターミナル
$ git push -u origin master

上のコードを実行した結果…

ターミナル
unable to access 'https://github/moutoon/hello_app.git/': Could not resolve host: git hub

[ 翻訳 ]
'https://github/moutoon/hello_app.git/' にアクセスできません。ホストを解決できませんでした: git hub

原因はプロキシサーバを経由してGitHubへ接続しているらしい。
色々調べてみましたが解決できず、httpsからsshに変更することにしました。

  • 新しいSSH Keyを作成
  • GitHubアカウントにSSH Keyを登録
  • Gitのリモートリポジトリをhttpsに設定しているのでsshに変更

無事解決!

参考にしたサイト・記事

おわりに

はじめて学習の記録をQiitaで書いてみましたが、めちゃくちゃ難しいなぁと実感しました。

  • どうしたら読みやすいか
  • 何を伝えたいのか
  • 効果的な見出しやマークダウン記法は何か
  • お硬い文章になるのでもう少しユーモアを入れたい

いろいろ考えながら文章を書くと大変ですが、完璧でなくても書ききった自分を褒めてあげたいと思います。

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

#Rails redirect_toではhttpリクエストをgetからdeleteに変更できない!?

Rails redirect_toではhttpリクエストをgetからdeleteに変更できない!?

rails 5.2.2
を使ってます。

今回はユーザーの退会ボタンを押したら退会処理がなされてかつ、ログアウトされ、ログイン画面に飛ばすような設計にしてそれを実装しようとしていたのですが、そこで問題が発生しました!!

それは退会処理後のログアウトを呼び出そうとして
redirect_to user_sessions_path
を実行したところ、「httpリクエストがgetですよ」とのことでdeleteに修正しようとしたが
残念
redirect_to user_sessions_path, method: :delete
ではできなかった
これ以上解決方法が浮かばなかったのでいろいろ調べたところ
deleteになってしまった場合はstatusコードを編集することでgetにできるらしいが
getからdeleteは無理なようだ・・・
postは指定できないことは知っていたがまだredirect_toに弊害があったとは・・・

皆さんもきおつけてくださいね
僕はこれに2時間近く囚われてしまい悔しいです笑笑

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

Docker ComposeでApache+Rails環境を構築

最終的なフォルダ構成

最終的なフォルダ構成
myapp/
    |--docker-compose.yml
        |--apache/
                |--html/
    |--rails/
        |--app/
        |--bin/
        |--config/
        |--db/
        |--lib/
        |--log/
        |--public/
        |--storage/
        |--test/
        |--tmp/
        |--vendor/
        |--config.ru
        |--Dockerfile
        |--entrypoint.sh
        |--Gemfile
        |--Gemfile.lock
        |--Rakefile
    |--README.md
        |--.env
        |--.gitignore
        |--.git

.git

Gitの初期化を行う

bash
git init

.gitignore

  • Gitの管理下に置きたくないファイルやディレクトリを管理するためのファイル
.gitigrore
/.env

.env

  • 環境変数を管理するために使われるファイル
.env
# commons
WORKDIR=app
CONTAINER_PORT=3000
RAILS_PORT=3000
APACHE_PORT=8080

# db
# POSTGRES_PASSWORD=password

docker-compose.yml

まずプロジェクトフォルダを作成し、そこにrailisフォルダ、apacheフォルダとdockercompose.ymlファイルを作成する。

docker-compose.yml
version: '3.8'

services:

  apache:
    image: httpd:alpine
    container_name: apache
    volumes:
      - ./apache/html:/usr/local/apache2/htdocs
    # httpdイメージでは80番ポートを公開している
    ports:
      - "$APACHE_PORT:80"
    depends_on:
      - rails

  # railイメージを構築、railsコンテナを構築、起動
  rails:
    # railsフォルダの中にあるDockerfileをビルド
    build:
      context: ./rails
      # Dockerイメージをビルドする際にDockerfileに渡す値を指定
      args:
        WORKDIR: $WORKDIR
    container_name: rails
    environment:
      # rack-corsで使用する
      ACCESS_CONTROL_ALLOW_ORIGIN: "localhost:$APACHE_PORT"
    # サーバの起動フラッグを削除(起動されていると勘違いしてしまわないように)
    # ポートを$CONTAINER_PORTとしてサーバ起動(Dockerfile内でも起動しているが)
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p $CONTAINER_PORT -b '0.0.0.0'"
    # コンテナ中のappフォルダをローカルのappフォルダに結びつける(コンテナを起動する際にホストマシン上のデータがコンテナにマウントされる)
    volumes:
      - ./rails:/$WORKDIR
    # コンテナのポートとホストのポートを対応付ける
    ports:
      - "$RAILS_PORT:$CONTAINER_PORT"
    # attachに必要,ターミナルの入出力がコンテナのbashとつながる?,attachしないとブラウザは一生読み込み中に
    stdin_open: true
    # これがないとattachしてもすぐ切れてしまう?
    tty: true

CORSの設定

  • CORSとは、異なるオリジン感の通信を許可する仕組み

    1. Gemのインストール
      1. railsディレクトリのGemfileを開く
      2. 26行目付近のgem 'rack-cors'のコメントを外す
      3. Dockerイメージを再ビルド
    2. 設定ファイルの編集

      1. api/config/initializers直下にあるcors.rbが設定ファイル
      2. 下記のように編集

        cors.rb
        # Be sure to restart your server when you modify this file.
        
        # Avoid CORS issues when API is called from the frontend app.
        # Handle Cross-Origin Resource Sharing (CORS) in order to accept cross-origin AJAX requests.
        
        # Read more: https://github.com/cyu/rack-cors
        
        Rails.application.config.middleware.insert_before 0, Rack::Cors do
          allow do
            # docker-compose.ymlで指定した値かその値がNullの場合は、空文字を入れる(エラーにならないように)
            origins ENV["ACCESS_CONTROL_ALLOW_ORIGIN"] || ""
        
            # 許可したいリソースファイル
            resource '*',
              headers: :any,
              # getのみ許可
              methods: [:get]
          end
        end
        

Dockerfile

railsイメージはDockerfileから作成するためDockerfileとentrypoint.shをrailsフォルダの下に作成する。

現在のフォルダ構成
myapp/
    |--docker-compose.yml
        |--apache/
    |--rails/
      |--Dockerfile
      |--entrypoint.sh

Dockerfile
FROM ruby:2.7.2

RUN apt update -qq && \
    apt install -y build-essential \
    libpq-dev

# Dockerfile内で使用する変数名を指定    
ARG WORKDIR
ENV APP_ROOT /$WORKDIR
RUN mkdir $APP_ROOT
WORKDIR $APP_ROOT
# このファイル(Dockerfile)と同じ階層にあるGemfile等をコンテナにコピー
COPY ./Gemfile $APP_ROOT/Gemfile
COPY ./Gemfile.lock $APP_ROOT/Gemfile.lock
# コンテナのGemfileを参考にGemをインストール
RUN bundle install
# このファイルが含まれているディレクトリをコンテナのルートディレクトリにコピー
COPY . $APP_ROOT

COPY ./entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]

CMD ["rails", "server", "-b", "0.0.0.0"]
entrypoint.sh
#!/bin/bash
set -e

# Remove a potentially pre-existing server.pid for Rails.
rm -f /app/tmp/pids/server.pid

# Then exec the container's main process (what's set as CMD in the Dockerfile).
exec "$@"

GemfileとGemfile.lock

現在のフォルダ構成
myapp/
    |--docker-compose.yml
        |--apache/
    |--rails/
      |--Dockerfile
      |--entrypoint.sh
      |--Gemfile
      |--Gemfile.lock

Gemfile
source 'https://rubygems.org'
gem 'rails', '6.0.0'

GemfileはRubyのライブラリをまとめたもので、Dockerfile内のbundle installではこのファイルを元にgemたちをインストールしている。
Gemfile.lockはGemfileをもとに実際にインストールされたgemの一覧とバージョンが記載されたファイルで、ここでは空のファイルを作成しておく。

イメージの構築、コンテナの作成・起動

ここまででとりあえず必要なファイルが揃ったので、イメージの構築等を行っていく。

rails new —api

rails newはrailsアプリケーションの雛形を作成してくれるコマンドで、railsサーバを立ち上げるのに必要なgem等を作成してくれる。今回railsはAPIサーバとして用いるためAPIモードでアプリを作成するように--apiをオプションとして付けている。

bash
docker-compose run rails rails new . --api --force --skip-bundle

docker-compose runは引数で指定したサービスについて、イメージの構築から、コンテナの作成・起動までを行ってくれるコマンドで、それプラスサービス名の後にコマンドを打つとコンテナ内で実行してくれる。
ここではサービスにDockerfileで指定したrails:を指定しており、プラスアルファのコマンドとしてrails new .を行っている。

--forceは既存のGemfileを上書きするためのオプションである。
後で、イメージを構築する際にbundle installは行われるので、ここでは--skip-bundleとしている。

イメージの構築

bash
docker-compose build

このとき、Dockerfileよりrailsのイメージが作成される。

コンテナの作成・起動

bash
docker-compose up

httpdのイメージはこのときに、Docker Hubからローカルに持ってきている。

localhost:8080でapache/htmlにアクセスできる。localhost:3000でrailsサーバにアクセスできる。

補足

docker-compose buildbundle installは行われるが、volumeとは紐付かないらしく、コンテナを起動する際にエラーが出る恐れがある。そのときは、volumeと紐づけられるrunコマンドを実行する。

$ docker-compose run rails bundle install

または、rails new--skip-bundleを付けなければ問題ない。

Docker Compose + Railsでイメージ内でbundle installしているはずなのにgemが無いとエラーがでる。 - Qiita

参考

DockerインストールからRails + Docker + MySQLで環境構築までの手順
【Mac編】DockerでRuby on Railsの開発環境を作ってみよう
Docker ComposeでNode.jsの環境構築 - Qiita
Rails6・Nuxt.js・PostgreSQLを動かすdocker-compose.ymlファイルを作成する - 独学プログラマ

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

【Rails】【Controllerから外部APIを叩く】 OpenWeatherMap APIで天気情報を取得

はじめに

外部APIを叩くという経験が初めてだったので、どんな大変な作業が待っているのだろうと心構えをしていたのですが、驚いたことに
実は難易度が低くて、高機能が実装できるハイコスパな方法でした。
今回実装した機能の概要は以下の通りです。メモ程度ですが参考程度に載せておきます。

API概要.jpg

前提

  • deviseでユーザーに関する機能作成済み
  • food_record(料理記録)モデルを作成済み
  • new, createなどの基本的な機能は既に実装済み

環境構築

  • Ruby2.7
  • Ruby on Rails6

API_KEYの取得

OpenWeatherMapで会員登録して、API_KEYを取得します。こちらは、多くの記事が出回っているので、割愛させて頂きます。
https://qiita.com/matsubishi5/items/fcd77eacb0ed111299e2
↑私はこちらを参照しました。

http://api.openweathermap.org/data/2.5/weather?q=Tokyo&appid=
末尾に取得したAPI_KEYを貼り付けて、ブラウザで実行してみてください。東京の今の天気が返ってくるはずです。これだけで、APIってこんなものかと実感できますし、疎通確認の意味でもやっておいた方が無難です。
ちなみに、会員登録からAPI_KEYが使えるようになるまで数時間程度かかります。(いくぞうは、3時間くらいでした)

必要なライブラリをインストール & API_KEYを格納

Gemfile.
#API_KEYを環境変数として管理する(Keyを外部流出させないための措置)
gem 'dotenv-rails'
#アプリケーション内でHTTPリクエストを投げたい場合に使うクラス
gem 'httpclient'

Gemfile記載後、bundle installします。



..env.
OPEN_WEATHER_MAP_API = 'API_KEY貼り付け'
URI = 'https://api.openweathermap.org/data/2.5/weather'

.envには、前もって取得したAPI_KEYをコピペします。

.gitignore.
/.env

ここで絶対に忘れずに.gitignoreを作ってください。.envをgithubにpushした場合、全世界にAPI_KEYを晒すことになってしまいます。(私は1回やらかしました、、、)

APIを叩く処理

controllerでAPIを叩きます。
- Api::OpenWeatherMap::Requestという処理がありますが、こちらで、下記モジュール(request.rb)を呼び出しています。
- コードがブサイクでごめんなさい!!!!でもちゃんと動きます。

food_records_controller.rb
def create

    #料理記録
    @food_record = current_user.food_records.build(food_record_params)
    @food_record.food_date = Time.zone.today

    #天気API
    #リクエストを出す
    open_weather = Api::OpenWeatherMap::Request.new(current_user.location_id)
    #戻り値を受け取る
    response = open_weather.request
    if @food_record.valid?
      #APIが正常に動作した場合
      if response['cod'] == 200
        params_weather = Api::OpenWeatherMap::Request.attributes_for(response)
        @food_record.update(params_weather)
        flash[:notice] = "登録に成功しました"
      else
        #APIが正常に動作しない場合も料理情報は記録する
        flash[:notice] = "天気情報の取得に失敗しましたが、登録に成功しました"
      end
      redirect_to root_url
    else
      render 'new'
    end
  end



- 処理本体は、モジュールに書きました。
- 今回、取得する天気の地域としてlocation_idを使用していますが、'id: location_id'をq: 'Tokyo'というように変えても上手くいきます。

lib/api/open_weather_map/request.rb
module Api
  module OpenWeatherMap
    class Request
      attr_accessor :query

      def initialize(location_id)
        @query = {
          id: location_id,
          units: 'metric',
          appid:
          'OPEN_WEATHER_MAP_API']
        }
      end

      def request
        client = HTTPClient.new
        request = client.get(ENV['URI'], query)
        JSON.parse(request.body)
      end

      # 取得したjsonをparamsに変換
      def self.attributes_for(attrs)
        {
          weather_main: attrs['weather'][0]['main'],
          weather_description: attrs['weather'][0]['description'],
          weather_icon: attrs['weather'][0]['icon'],
          weather_id: attrs['weather'][0]['id'],
          temp: attrs['main']['temp'],
          temp_max: attrs['main']['temp_max'],
          temp_min: attrs['main']['temp_min'],
          humidity: attrs['main']['humidity'],
          pressure: attrs['main']['pressure']
        }
      end
    end
  end
end



lib以下はデフォルトではtask以外読み込まれないので、config/application.rbに、以下の設定が必要です

config/application.rb
config.paths.add 'lib', eager_load: true

完成

スクリーンショット 2021-01-21 21.32.30.png

<%= image_tag "http://openweathermap.org/img/wn/#{@food_record.weather_icon.chop}d@2x.png" %>
アイコンはこのように表示させています。chop処理をしているのは、夜に天気を取得すると、太陽が黒色になってしまうからです。
{@food_record.weather_icon.chop}d このようにchopで末尾のnを削って、d「昼間(おそらくdaytime)」を付け足しています。

補足

  • 今回はlocation_idを使用しました。私は、下記のCSVを読み込ませて、cityテーブルに初期データとして投入して使用しています。
  • 都市名, location_id, 緯度, 経度 の順になっています。
  • 説明は割愛しますが、下記のようにすれば簡単に実装できます。
db/csv/cities.csv
札幌,2128295,141.346939,43.064171
青森,2130658,140.740005,40.82444
盛岡,2111834,141.152496,39.703609
仙台,2111149,140.871933,38.26889
秋田,2113126,140.116669,39.716671
山形,2110556,140.363327,38.240559
福島,2112923,140.467773,37.75
水戸,2111901,140.446671,36.341389
宇都宮,1849053,139.883606,36.56583
前橋,1857843,139.060837,36.391109
さいたま,1853226,35.857208
千葉,2113015,140.123337,35.604721
東京,1850147,139.691711,35.689499
横浜,1848354,139.642502,35.447781
新潟,1855431,139.023605,37.902222
富山,1849876,137.211395,36.695278
金沢,1860243,136.625565,36.59444
福井,1863983,136.225174,35.850101
山梨,1848649,138.608002,35.61602
長野,1856210,138.040771,36.13464
岐阜,1863640,137.053986,35.78091
静岡,1851715,138.325424,35.025219
名古屋,1856057,136.906403,35.181469
津,1849796,136.508606,34.730282
大津,1853574,135.868332,35.00444
京都,1857910,135.753845,35.021069
大阪,1853909,135.502182,34.693741
神戸,1859171,135.182999,34.691299
奈良,1855612,135.804855,34.685051
和歌山,1926004,135.167496,34.226109
鳥取,1849890,133.850815,35.367859
松江,1857550,133.050568,35.472221
岡山,1854383,133.934998,34.661671
広島,1862415,132.459366,34.396271
山口,1848689,131.47139,34.185829
徳島,1850158,134.559433,34.06583
高松,1851100,134.043335,34.340279
松山,1926099,132.765747,33.839161
高知,1859146,133.531113,33.559719
福岡,1863967,130.41806,33.606392
佐賀,1853303,130.298798,33.249321
長崎,1856177,129.873611,32.74472
熊本,1858421,130.741669,32.789719
大分,1854487,131.612503,33.23806
宮崎,1856717,131.423889,31.91111
鹿児島,1860827,130.558136,31.560181
那覇,1856035,127.681107,26.2125

db/seeds.rb
require "csv"
CSV.foreach('db/csv/cities.csv') do |row|
  City.create(
    name: row[0],
    location_id: row[1],
    lon: row[2],
    lat: row[3]
  )
end



上記ファイルを作って、cityテーブルも作成済みならば、下記コマンドを実行します。

rails db:seed

最後に

少しでも参考になればLGMTよろしくお願いします。
また、説明不十分な点や、説明が間違っている箇所が御座いましたら、コメントいただけると幸いです。

Twitterもやっているので、よかったらフォローお願いします!!
https://twitter.com/engineer_ikuzou

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

Rubocopの導入

はじめに

オリアプ制作でRubocopを導入したので、載せておこうと思います。
( Rubocop:静的コード解析ツールのことで、余分なスペースやインシデントが揃っていないなどを指摘・修正してくれるGem )
オリアプのコードは誰かに直接見てもらう事はないので、導入する必要はないかなと思ったのですが、今後使用するかもしれないと思い、導入しました。

1.Gemの導入・ファイル作成

1.Gemfileにrubocopを記述し、bundle installする。
コードはgroup :development do ~ end 内に記述する。

Gemfile
group development do
  #中略
  gem 'rubocop', require: false
end

2.rubocopの設定コードを記述するファイル ( .rubocop.yml ) をプロジェクト直下に作成する。

2.Rubocopの設定

作成した .rubocop.yml に設定用のコードを記述していく。
以下のコードはあくまで一例であり、調べればまだある。

.rubocop.yml
AllCops:
 Exclude:   # 除外するディレクトリ
   - "vendor/**/*" 
   - "db/**/*"
   - "config/**/*"
   - "bin/*"
   - "node_modules/**/*"
   - "Gemfile"

# メソッドの行数が多すぎないかをチェック
Metrics/MethodLength:
 Max: 30

# クラスの行数をチェック(無効)
Metrics/ClassLength:
 Enabled: false

# ブロック内の行数をチェックする
Metrics/BlockLength:
 Exclude:    # チェックの対象から除外
   - "spec/**/*"

# 下記項目を計算して基準値を超えると警告する(下記頭文字をとって'Abc')
# ・Assignment: 変数への代入  ・Branch: メソッド呼び出し  ・Condition: 条件文
Metrics/AbcSize:
 Max: 50

# メソッドの中身が複雑になっていないか、計算して基準値を超えると警告する
Metrics/PerceivedComplexity:
 Max: 8

# 1行あたりの文字数をチェックする
Layout/LineLength:
 Max: 130
 Exclude:    # チェックの対象から除外
   - "Rakefile"
   - "spec/rails_helper.rb"
   - "spec/spec_helper.rb"

# 循環的複雑度が高すぎないかをチェック(ifやforなどを1メソッド内で使いすぎている)
Metrics/CyclomaticComplexity:
 Max: 10

# ネストが深すぎないかをチェック(if文のネストもチェック)
Metrics/BlockNesting:
 Max: 5

# メソッドパラメータ名の最小文字数を設定
Naming/MethodParameterName:
 MinNameLength: 1

# 日本語でのコメントを許可
Style/AsciiComments:
 Enabled: false

# クラス内にクラスが定義されていないかチェック(無効)
Style/ClassAndModuleChildren:
 Enabled: false

# 文字列に値が代入されて変わっていないかチェック(無効)
Style/FrozenStringLiteralComment:
 Enabled: false

# クラスやモジュール定義前に、それらの説明書きがあるかをチェック(無効)
Style/Documentation:
 Enabled: false

# %i()構文を使用していないシンボルで構成される配列リテラルをチェック(無効)
Style/SymbolArray:
 Enabled: false

# 空メソッドの場合に、1行のスタイルにしない
Style/EmptyMethod:
 EnforcedStyle: expanded

3.Rubocopの実行

以下のコマンドを実行することでRubocopが余分な箇所などを指摘してくれる。

ターミナル
% bundle exec rubocop

また、以下のコマンドを実行することでRubocopがさきほどの指摘を自動で修正してくれる。

ターミナル
% bundle exec rubocop -a

以上、導入完了!!!

最後に

rubocopの設定用のコードを覚えるのは無理なので、もし使う機会があれば、この投稿を参考に導入していきたいと思います。

参考

以下の投稿を参考にさせて頂きました。ありがとうございました。
https://qiita.com/tomohiii/items/1a17018b5a48b8284a8b

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

[Rails] gem jsonを使って、APIを叩く。

今回は、weather APIにしました。(https://openweathermap.org/)

APIの取得方法

gem json
def home
    require 'net/http'
    require 'json'

    @url = 'http://api.openweathermap.org/data/2.5/weather?q=berlin,de&appid=ここにapikey。'
    @uri = URI(@url)
    @response = Net::HTTP.get(@uri)
    @output = JSON.parse(@response)
   end

output

<%= @output %>

です。

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

RailsでPostgreSQLを使うぞ!「FATAL: role "postgres" does not existの解消」にむけて

はじめに

Railsチュートリアなんとか一通り終え(理解度としては3割程度なので終えたと言えるかどうかはおいといて...)、
自身のローカル環境にてRuby, Railsの環境を構築する際に発生した問題に関して、自身のメモのため、初学者の方で同様の問題が発生した方向けに投稿します!

開発環境

  • macOS Catalina 10.15.7
  • MacBook Pro (13-inch, 2020, Two Thunderbolt 3 ports)
  • プロセッサ 1.4 GHz クアッドコアIntel Core i5
  • メモリ 8GB

  • Ruby 2.5.7

  • Rails 5.2.3

  • PostgreSQL 13.1

導入までの一連の流れに関して

一連の環境構築までの流れに関しては下記記事が大変参考になると思いますので、ぜひ見てください。
今回はPostgreSQLの導入部にフォーカスして記事を作りたいと思います!

【完全版】MacでRails環境構築する手順の全て
https://qiita.com/kodai_0122/items/56168eaec28eb7b1b93b

エラー FATAL: role "postgres" does not exist の発生

ここからが本題です!
一通り上記記事を参考に環境構築を終え、railsを起動しようとしたところ、

$ rails s

下記のようなエラーが発生し、railsをが起動できない状況に...

スクリーンショット 2021-01-20 22.14.39(2).png

ん?そもそもロールってなに?
詳しくは下記が参考になると思いますが、

PostgreSQL 11.5文書
https://www.postgresql.jp/document/11/html/role-attributes.html

「PostgreSQLは、ロールという概念を使用してデータベースへの接続承認を管理する」らしい。
つまり、role postgres does not existというのは、ロール(データベースへアクセスする権限みたいなもの?)として、postgreがないよっていことで解釈しました。

そこで実際に下記コマンドを使用して、データベースのロール状態を確認すると...

$ psql postgres
psql (11.4)
Type "help" for help.

postgres=# \du         # 現在のロール確認(ちなみにバッククオート\は「option+¥」キーですよ)
Role name   |                         Attributes                         | Member of 
---------------+------------------------------------------------------------+-----------
 araishuntarou | Superuser, Create role, Create DB, Replication, Bypass RLS | {}
               |                                                        | {}

やはりRole nameにpostgresがない!!

では、ここのroleにpostgresを追加すれば全てが解決できるぞと思い、
コマンド入力! そしてすかさず確認!

$ createuser postgres 
$ psql postgres
psql (11.4)
Type "help" for help.

postgres=# \du         # 現在のロール確認(ちなみにバッククオート\は「option+¥」キーですよ)
Role name   |                         Attributes                         | Member of 
---------------+------------------------------------------------------------+-----------
 araishuntarou | Superuser, Create role, Create DB, Replication, Bypass RLS | {}
 postgres      |                                                            | {}

やったー!roleにpostgresが追加できてる。うれしー。これで解決と思いました...。

第二の関門 PG::InsufficientPrivilege: ERROR: permission denied to create database

やっとの思いで、railの起動まで辿り着きましたが、

$ rails s

なんとまたエラーが発生!!心折れます....。
PG::InsufficientPrivilege: ERROR: permission denied to create database
う~ん、データベースを作る権限がないということかな?

言われてみれば確かに、postgresのAttributesに何の記述もされていないぞ。

Role name   |                         Attributes                         | Member of 
---------------+------------------------------------------------------------+-----------
 araishuntarou | Superuser, Create role, Create DB, Replication, Bypass RLS | {}
 postgres      |                                                            | {}

そこで、postgres にsuperuserの権限を与えようと下記コマンドを実施!
superuserは「ログイン以外に何でもできる権限をロールに与える」ため、危険な場合もあるとのこと。
(ここら辺はあとで勉強しよう... 本来は目的に応じた権限のみを与えた方がいいのかも)

参考
https://eng-entrance.com/postgresql-role

$ psql postgres
psql (11.4)
Type "help" for help.

postgres=# DROP ROLE postgres ;                   # 一度postgresを削除(役割だけを追加する方法が不明..)
postgres=# CREATE ROLE postgres SUPERUSER ;      # superuserの権限を持ったpostgresを作成!
postgres=# \du                                    # 現在のロール確認

Role name   |                         Attributes                         | Member of 
---------------+------------------------------------------------------------+--------- 
araishuntarou | Superuser, Create role, Create DB, Replication, Bypass RLS | {}
 postgres     | Superuser                                                  | {}

やっとここまできたか...

最後の関門 ActiveRecord::PendingMigrationError

datebaseをmigrateしてないですよという意味だと捉え、

$ rails db:create   #データベース自体(テーブルを保管しておく全体のシステム)を作る

からの

$ rails db:migrate    #データベースの中にテーブルを作ったり、カラムを変更したりするときに実行する

を実行!!

最後に

$ rails s

出来たー!!!!

スクリーンショット 2021-01-21 18.33.39(2).png

最後に

稚拙な文章にもかかわらず最後までお読みいただきまして誠にありがとうございました。
私も完全には理解できていないところが多く、これかわも勉強しなければなりませんが、
少同じ問題で困っている方々の助けに慣れれば幸いです!

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

複数の画像投稿/表示をさせたいとき(refile使用)

Railで作成するアプリ内で写真投稿や表示をさせたい時にrefileを使う方へ。
下記の手順で本来であれば1枚のみしか投稿・表示できないところ、
複数の写真を投稿・表示できるようになります。

1.rifleの準備

Gemfileに下記を記入後、「bundle install」ください。

gem "refile", require: "refile/rails", github: 'manfe/refile'
gem "refile-mini_magick"

2.テーブルの作成

複数枚の写真を投稿・表示させる場合、
「写真を表示させるモデル」と「写真を保存しておくテーブル」が必要になります。

db/schema.rb
create_table "shops", force: :cascade do |t|
  t.datetime "created_at", null: false
  t.datetime "updated_at", null: false
end

create_table "shop_images", force: :cascade do |t|
  t.string "shop_image_id"
  t.integer "shop_id"
  t.datetime "created_at", null: false
  t.datetime "updated_at", null: false
end

3.モデルの設定

先ほど作成したテーブルのモデルにて、それぞれの関係を1:多の関係で紐付ける必要があります。
(ここでは例として、「shopモデル」に紐づく「shop_imageモデル」で解説しています。
「shop」はあくまで例ですので、ご自身の作成されているモデル名に合わせて修正ください)

shop.rb
 belongs_to :shop
 #1:多の紐付け
 attachment :shop_image
 #shop_image(_id)をshopのviewにて表示させるための設定
shop_image.rb
 has_many :shop_images, dependent: :destroy
 #1:多の紐付け
 accepts_attachments_for :shop_images, attachment: :shop_image
 #shop_image(_id)をshopのviewにて表示させるための設定

4.コントローラーの設定

shopコントローラーのnewアクション(投稿)、showアクション(一覧表示)、
ストロングパラメータをそれぞれ記述していきましょう。(※shop_imageコントローラーは作成しません)

shops_cotroller.rb
def new
  @shop = Shop.new
  #空データを作成
  @shop.shop_images.new
  #写真の空データを作成
end

def show
  Shop.find(params[:id)
end

private
def shop_params
  params.require(:shop).permit(shop_images_shop_images: [])
  #shop_images_shop_images → shop_imagesテーブルの複数のshop_imageを指定
  #: [] → 指定した複数のshop_imageを配列に入れます(配列に入れて管理するため)
end

5.ビューの記述

投稿ページ(new.html)と投稿ページ(show.html)を記述していきましょう。

app/views/shops/new.html.erb
<%= form_with model: @shop, local:true do |f| %>
<table>
  <tbody>
    <td>
     <%= f.attachment_field :shop_images_shop_images, multiple: true %>
    </td>
  </tbody>
</table>
<% end %>

↑「multiple:true」にすることで複数投稿が可能になります。

app/views/shops/show.html.erb
<table>
  <td>
  <% if @shop.shop_images.present? %>
    <% @shop.shop_images.each do |shop_image| %>
      <%= attachment_image_tag shop_image, :shop_image, :fill, 200, 200 %>
    <% end %>
  <% else %>
    <%= image_tag "no_image.jpg", size: "200x200" %>
  <% end %>
  </td>
</table>

↑「no_image.jpg」は別途用意していただく必要があります。
「app/assets/images」に用意頂いたデータを入れてください。
no_image_jpg (urlを開いていただいた後、右クリックでダンロードください)

6.ブラウザ上にて複数の画像を選択する方法

複数の画像をアップロードする場合は、「ファイル選択」をクリック後、
Shift + クリック, または Ctrl + クリックで画像を選択ください。

参考に記事

refileの基本と複数画像のアップロード
【Ruby on Rails】refileで複数画像のアップロード

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

Formオブジェクトを用いて作成したデータを、編集・更新する方法

要点

  • 初心者がアプリを作るときの参考に
  • formオブジェクトを用いた編集・更新機能は複雑なので詳しく知りたいorおさらいしたい
  • 下記のエラーを解消する方法
param is missing or the value is empty: 'Formオブジェクト名'

はじめに

メルカリのようなフリマアプリを作成中で、Formオブジェクトを用いて商品にタグ付けして編集・更新(edit・update)する機能を実装まで行いました
各モデルとコントローラーは以下のようになります

  • Item/商品
  • Tag/タグ
  • TagItemRelation/商品とタグの中間テーブル
  • TagsItem/ ItemとTagを同時に保存するためのFormオブジェクト
/app/model/item.rb
class Item < ApplicationRecord
  has_many :tag_item_relations, foreign_key: :item_id, dependent: :destroy
  has_many :tags, through: :tag_item_relations
end
/app/model/tag.rb
class Tag < ApplicationRecord
  has_many :tag_item_relations
  has_many :items, through: :tag_item_relations
  validates :tag_name, uniqueness: true
end
/app/model/tag_item_relation.rb
class TagItemRelation < ApplicationRecord
  belongs_to :item
  belongs_to :tag
end
/app/form/tags_item.rb
class TagsItem
  include ActiveModel::Model
  attr_accessor :item_name,
                :tag_name

  with_options presence: true do
    validates :item_name
  end

  def save
    item = Item.create(item_name: item_name)
    tag = Tag.where(tag_name: tag_name).first_or_initialize
    tag.save
    TagItemRelation.create(item_id: item.id, tag_id: tag.id)
  end
end

コントローラー

/app/controller/items_controller.rb
class ItemsController < ApplicationController
   def index
     @items = Item.all.order('created_at ASC')
   end

   def edit
    @item = current_user.items.find(params[:id])
    @tegs_item = TagsItem.new(item: @item)
   end

   def update
    @item = current_user.items.find(params[:id])
    @tags_item = TagsItem.new(update_items_params, item: @item)
     if @tags_item.valid?
       @tags_item.save
       redirect_to root_path
     else
       render :edit
     end
   end

  private

  def update_items_params
    params.require(:item).permit(
      :item_name,
      :tag_name
    )
  end
end

しかし実際に商品を編集・更新をしてみると、、、

param is missing or the value is empty: 'tags_item'

のエラーが出てしまい、商品の更新ができませんでした

調べたこと

エラー内容を読んでみると生成したFormオブジェクトの'tag_item_relation'が空になっているとのこと
そして、binding.pryなどを使って調べてみると、そもそもこの記述では'tags_item'編集・更新では機能していないことが分かります
さらに原因を調べてみると新規投稿(new・create)と編集更新(edit・update)の機能を仕分けしていなかったことだと分かりました

Formオブジェクトで編集更新

今回のエラーの原因はFormオブジェクトに記載したsaveが新規投稿と編集更新で仕分けされていなかったことだと分かりました

/app/form/tags_item.rb
class TagsItem
  include ActiveModel::Model
  attr_accessor :item_name,
                :tag_name

  with_options presence: true do
    validates :item_name
  end

  def save
    item = Item.create(item_name: item_name)  #ここがcreateアクションのみ
    tag = Tag.where(tag_name: tag_name).first_or_initialize
    tag.save
    TagItemRelation.create(item_id: item.id, tag_id: tag.id)
  end
end

この記述を

/app/form/tags_item.rb
class TagsItem
  include ActiveModel::Model
  attr_accessor :item_name,
                :tag_name

  with_options presence: true do
    validates :item_name
  end

  # itemがすでに保存されているものか、新規のものかで、PUTとPATCHを分ける
  delegate :persisted?, to: :item


  # initializeでFormオブジェクトの値を初期化し、更新の際はdefault_attributesを呼び出す設定
  def initialize(attributes = nil, item: Item.new)
    @item = item
    attributes ||= default_attributes
    super(attributes)
  end

  def save
    return if invalid?

    ActiveRecord::Base.transaction do
      # mapメソッドを使いsplit_tag_namesをtagの情報に変換
      tags = split_tag_names.map { |tag_name| Tag.find_or_create_by!(tag_name: tag_name) }
      item.update!(item_name: item_name, tags: tags)
    end
  rescue ActiveRecord::RecordInvalid
    false
  end

  #  formを飛ばす場所を(#createか#updateか)を判別して、切り替えている
  def to_model
    item
  end

  private

  attr_reader :item

  def default_attributes
    {
      item_name: item.item_name,
      tag_name: item.tags.pluck(:tag_name).join(',')
    }
  end

  def split_tag_names
    tag_name.split(',')
  end
end

に変更することで無事にタグ付け機能の編集更新を行うことができました

参考にしたサイト

Railsのデザインパターン: Formオブジェクト

formオブジェクトパターンで、タグ付け機能を実装

関連記事

Formオブジェクトを用いて作成したデータを、特定のデータのみ削除する方法←(前回記事)

railsでタグ機能を実装する

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

[Rails]DRYに書きたい(可読性、保守性)

[Rails]似たようなコードを1つにまとめたい(パーシャル)
https://railstutorial.jp/chapters/filling_in_the_layout?version=5.1#sec-partials

[Rails]DRYなコードを書きたいが、一部分が違う場合
https://railstutorial.jp/chapters/updating_and_deleting_users?version=5.1#sec-edit_form

[Rails]タイトルの可変要素をDRYする
https://railstutorial.jp/chapters/static_pages?version=5.1#sec-layouts_and_embedded_ruby
https://qiita.com/shumpeism/items/a0ad5930fa3bc0d24c70

[Rails]汎用性があるので異なるControllerでも定義したメソッドを使いたい
継承→application_controller.rb
https://railstutorial.jp/chapters/user_microposts?version=5.1#sec-micropost_access_control

[Rails]記法一覧
https://qiita.com/gakkie/items/3afcd505c786364aa5fa
https://blog.mothule.com/ruby/ruby-percent-syntax

[Ruby]記法一覧
https://railstutorial.jp/chapters/rails_flavored_ruby?version=5.1#cha-rails_flavored_ruby

[Rails]キーワード引数とオプション引数の違い
https://railstutorial.jp/chapters/updating_and_deleting_users?version=5.1#sec-users_index

Railsが暗黙的にやってくれること一覧
Railsは慣習に従って、アクションに対応するビューを暗黙的に呼び出します。例えば、showアクションの最後でshow.html.erbを呼び出す、といった具合です。

その他
[Rails]rails sがバックグラウンドにあり、ctrl+cで消せない場合
https://qiita.com/kanuu/items/fd6e33fca6ad6a90d059

[Rails]ユニーク制約とは?どんなメリットがある?

[Rails]シンボルを使うタイミング
https://uxmilk.jp/25934

[Rails]attr_accessor とは?
http://bryankawa.hatenablog.com/entry/2017/01/28/150537

非同期で通信がしたい→Ajax,JS
https://railstutorial.jp/chapters/following_users?version=5.1#sec-a_working_follow_button_with_ajax

http://railscasts.com/episodes/205-unobtrusive-javascript

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

[Rails]認可、認証、権限関連

[Rails]ログイン済みのユーザーだけにページを表示させたい
Controller
before_action :logged_in_user, only: [:edit, :update]
https://railstutorial.jp/chapters/updating_and_deleting_users?version=5.1#sec-authorization

[Rails]自分だけを編集したい
https://railstutorial.jp/chapters/updating_and_deleting_users?version=5.1#sec-requiring_the_right_user

[Rails]ユーザーがログインした後、ログイン直前に閲覧していたページヘとリダイレクトさせる機能
→ログインユーザー専用のページのURLにアクセスしたい→ログインする→さっき見てたログイン専用のページに行ける
フレンドリーフォワーディング request.url
https://railstutorial.jp/chapters/updating_and_deleting_users?version=5.1#sec-friendly_forwarding
[Rails]一つ前のURLを返したい(リダイレクトしたい) request.referrer
https://railstutorial.jp/chapters/user_microposts?version=5.1#sec-destroying_microposts
[Rails]認可と認証の違い
https://qiita.com/kaysquare1231/items/c4e4736f2a924b03777b

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

[Rails]セキュリティ系

[Rails]ストロングパラメーター(Strong parameters)
https://railstutorial.jp/chapters/updating_and_deleting_users?version=5.1#sec-revisiting_strong_parameters
[Rails]マスアサインメント
→リクエストのデータをそのままデータベースに保存すること(セキュリティ的に弱い)
https://thinkit.co.jp/story/2015/09/03/6389

[Rails]作業を元に戻したい!
rails destroy controller ~
https://railstutorial.jp/chapters/static_pages?version=5.1#sec-generated_static_pages

[Rails]アカウントの有効化
https://railstutorial.jp/chapters/account_activation?version=5.1#cha-account_activation

[Rails]SSL
ローカルのサーバーからネットワークに流れる前に、大事な情報を暗号化する技術
https://railstutorial.jp/chapters/sign_up?version=5.1#sec-ssl_in_production

[Rails]CSRF対策[Controller]
protect_from_forgery with: :exception
https://qiita.com/tanaka7014/items/5b4e2204dc6bec83c90e

[Rails]プレースホルダー
https://wa3.i-3-i.info/word118.html

[Rails]パスワードをハッシュ化,暗号化して保存したい
https://railstutorial.jp/chapters/modeling_users?version=5.1#sec-adding_a_secure_password

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

[Rails]DB,ActiveRecord系

[Rails]カラム名を変更したい
https://qiita.com/libertyu/items/93acd8733e34b1d0a63c

[Rails]シードを作る(サンプルユーザーをDBにたくさん作りたい)
https://qiita.com/takehanKosuke/items/79a66751fe95010ea5ee
https://railstutorial.jp/chapters/updating_and_deleting_users?version=5.1#sec-sample_users
https://teratail.com/questions/7371

[Rails]マイグレーションファイルを削除する
https://qiita.com/tanaka-t/items/cd6aa0526725e88f5024

[Rails]ActiveRecordのSQLの可読性をあげたい →Scope
https://qiita.com/ngron/items/14a39ce62c9d30bf3ac3

[Rails]複数テーブルにまたがる検索をしたい
https://qiita.com/leon-joel/items/f26556c9e56833983856
https://qiita.com/makitokezuka/items/f13b2e7bad77b5594911
[Rails]複数テーブルにまたがる検索をするときにカラムが被る時

[Rails]中間テーブルの命名規則
https://qiita.com/tkawa/items/dc3e313021f32fd91ca6
https://ja.stackoverflow.com/questions/12618/rails%E3%81%AE%E4%B8%AD%E9%96%93%E3%83%86%E3%83%BC%E3%83%96%E3%83%AB%E3%81%AE%E5%91%BD%E5%90%8D%E8%A6%8F%E5%89%87%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6

[Rails]多対多
https://qiita.com/Kohei_Kishimoto0214/items/cb9a3d3da57708fb52c9
[Rails]多対多のテーブルから検索や更新などしたい
https://qiita.com/Kohei_Kishimoto0214/items/cb9a3d3da57708fb52c9
https://qiita.com/ryutaro9595/items/e021eb789914cead4677

[Rails]中間テーブルのデータ保存
http://once-and-only.com/programing/ruby/%E3%82%A2%E3%82%BD%E3%82%B7%E3%82%A8%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E5%85%88%E3%81%AE%E3%83%86%E3%83%BC%E3%83%96%E3%83%AB%E4%BF%9D%E5%AD%98/

[SQL]データモデリング ER図
https://www.atmarkit.co.jp/ait/articles/0604/06/news110.html
https://jp.drinet.co.jp/blog/datamanagement/data_modeling_3minutes

[Rails]インデックス
インデックスを作成することでテーブルとは別に検索用に最適化された状態で必要なデータだけがテーブルとは別に保存される。検索用に並び替えをしている上、インデックスを貼ったカラムだけを検索できるので高速検索が可能。デメリットは、テーブルとは別に検索用のテーブルが保存される関係上データの追加に時間がかかること。なぜなら、データを追加するときに2つのテーブルを追加しなければならないから。
https://railstutorial.jp/chapters/user_microposts?version=5.1#sec-a_micropost_model
https://www.dbonline.jp/sqlite/index/index1.html
https://ja.wikipedia.org/wiki/%E7%B4%A2%E5%BC%95_%28%E3%83%87%E3%83%BC%E3%82%BF%E3%83%99%E3%83%BC%E3%82%B9%29

[Rails]親を削除した時に子も削除できることをしたい
dependent: :destroy
https://railstutorial.jp/chapters/user_microposts?version=5.1#sec-dependent_destroy

[Rails]リレーションの時の別テーブルのデータの呼び出し方
→micropost.userなど
https://railstutorial.jp/chapters/user_microposts?version=5.1#sec-destroying_microposts

[Rails]あるユーザーが同じユーザーを2回以上フォローすることを防ぐこと- 複合キーインデックス
add_index :relationships, [:follower_id, :followed_id], unique: true
の場合だと、follower_idとfollowed_idの組み合わせが必ずユニークであることを保証する仕組み
https://railstutorial.jp/chapters/following_users?version=5.1#sec-a_problem_with_the_data_model

[SQL]SQLの練習したい
https://paiza.hatenablog.com/entry/2019/12/22/SQL%E3%81%AE%E7%B7%B4%E7%BF%92%E3%81%AB%E6%9C%80%E9%81%A9%EF%BC%81%E3%83%96%E3%83%A9%E3%82%A6%E3%82%B6%E4%B8%8A%E3%81%A7%E5%AE%9F%E8%A1%8C%E3%81%A7%E3%81%8D%E3%82%8B%E5%88%9D%E5%BF%83%E8%80%85%E5%90%91

[Rails]seedにSecureramdomを導入したい
https://qiita.com/takuyanin/items/5334908b22156ec7290f

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

[Rails]ルーティング系

[Rails]localhost3000/の状態でトップページを表示したい(3)
→root
https://railstutorial.jp/chapters/static_pages?version=5.1#sec-setting_the_root_route

[Rails]RESTfulなルーティング
https://railstutorial.jp/chapters/sign_up?version=5.1#table-RESTful_users
https://qiita.com/NagaokaKenichi/items/0647c30ef596cedf4bf2

[Rails]resourceを使ったRESTfulなルーティングにRESTful以外の新しいアクションを追加したい。
https://qiita.com/ebihara99999/items/37afb1486442e7c16a8a
https://railstutorial.jp/chapters/following_users?version=5.1#sec-stats_and_a_follow_form
[Rails]link_toを使ってパラメーターを複数送る方法
https://poyopoyo0.hatenablog.com/entry/2016/02/29/100431

[Rails]resourseでネストをする際に、ネスト元もonly:createなどで制限したい
https://railsguides.jp/routing.html#rails%E3%83%AB%E3%83%BC%E3%82%BF%E3%83%BC%E3%82%92%E8%A8%AD%E5%AE%9A%E3%81%99%E3%82%8B

[Rails]id(並び順整数)をuuid(乱数)に変更したい(localhost:3000/user/ae6cdc44-68cd-4c76-b972-26d8a39b4bbb)みたいにしたい
https://qiita.com/params_bird/items/9e162fdcd9bdc2e42ccd

[Rails]ユーザー名(アカウント名)の初期設定を乱数にしたい。
https://teratail.com/questions/285445
username = SecureRandom.urlsafe_base64(10)

[Rails]パラメーターを追加で送信したい
hidden_tag

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

[Rails]Controller系

[Rails]フォームの入力に失敗した時の実装
https://railstutorial.jp/chapters/updating_and_deleting_users?version=5.1#sec-unsuccessful_edits

[Rails]Railsのredirect_toにおける_pathと_urlの使い分け(同じようなもの)
https://teratail.com/questions/204077

[Rails]部分テンプレートを呼び出したい
render ‘ファイル名’
https://pikawaka.com/rails/render

[Rails]Destroyアクションとセキュリティ
https://railstutorial.jp/chapters/updating_and_deleting_users?version=5.1#sec-the_destroy_action

[Rails]newとbuildの違い
newメソッドとbuildメソッドはともにインスタンスを生成するが、
buildは自動的にuser_idをセットしてインスタンスを生成することができる。
https://qiita.com/Kaisyou/items/8876f39e12631f4e5154

[Rails]CRUD(Create)
https://railstutorial.jp/chapters/sign_up?version=5.1#sec-signup_form

[Rails]CRUD(Read)
https://railstutorial.jp/chapters/updating_and_deleting_users?version=5.1#sec-showing_all_users

[Rails]CRUD(Destroy)
https://railstutorial.jp/chapters/updating_and_deleting_users?version=5.1#sec-the_destroy_action

[Rails]なぜ、HTMLフォームにcreateやdestroyのmethodが記載されてないのに、createされたりdestroyされるの?

①URLリクエストをroutesに送る。
②そこで該当コントローラーの該当アクションに指示が行き、そこで事前に自らが記載したcreateやdestroyの処理を行う。
自ら描く事もあれば、resourseで自動で書いてくれる人もいる。

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

[Rails]View系

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

[Rails]Model系

[Rails]ActiveRecordはデータベースはデータベースのレベルでは一意性を保証していない(validateで一意性を保証したとしても)
→indexをつければ解決
https://railstutorial.jp/chapters/modeling_users?version=5.1#sec-uniqueness_validation

[Rails]データベースに保存する前に保存する内容を検証したい - バリデーション
https://railstutorial.jp/chapters/modeling_users?version=5.1#sec-user_validations

[Rails]文字列などをランダムに出したい。乱数
https://docs.ruby-lang.org/ja/latest/class/SecureRandom.html

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

[Rails]機能要件の逆引きリファレンス

[Rails]検索機能
https://qiita.com/shin1rok/items/779e581e9d12a92310c3

[Rails]画像検索 - 外部ライブラリ Ransack
https://qiita.com/LuckOfWise/items/e020e896e71d47d0c6a4
http://nekorails.hatenablog.com/entry/2017/05/31/173925

[Rails]文章投稿機能
https://railstutorial.jp/chapters/user_microposts?version=5.1#cha-user_microposts

[Rails]画像を投稿したい
外部ライブラリ - CarrierWave
https://railstutorial.jp/chapters/user_microposts?version=5.1#sec-basic_image_upload
→投稿した画像が大きくなる
https://railstutorial.jp/chapters/user_microposts?version=5.1#sec-image_validation

[Rails]本番環境での画像の保存方法
→クラウドストレージを使うのがいい。
https://railstutorial.jp/chapters/user_microposts?version=5.1#sec-image_upload_in_production

[Rails]フォロー、フォロワー機能の骨組み
https://railstutorial.jp/chapters/following_users?version=5.1#sec-the_relationship_model

[Rails]フォロー、フォロワー一覧ページを作りたい
https://railstutorial.jp/chapters/following_users?version=5.1#sec-following_and_followers_pages

[Rails]フォローボタンを作りたい(リダイレクトとAjax)
https://railstutorial.jp/chapters/following_users?version=5.1#sec-a_working_follow_button_the_standard_way
https://railstutorial.jp/chapters/following_users?version=5.1#sec-a_working_follow_button_with_ajax

[Rails]タイムライン機能
https://railstutorial.jp/chapters/following_users?version=5.1#sec-the_status_feed

[Rails]タイムライン、フィード
https://railstutorial.jp/chapters/user_microposts?version=5.1#sec-a_proto_feed

[Rails]新規登録をしたらメールアドレスが送られるやつ
https://railstutorial.jp/chapters/account_activation?version=5.1#cha-account_activation

[Rails]パスワードを忘れた時に再設定できるやつ
https://railstutorial.jp/chapters/password_reset?version=5.1#cha-password_reset

[Rails]成功しました!失敗しました!を知らせてくれるやつ - フラッシュメッセージ
https://railstutorial.jp/chapters/sign_up?version=5.1#sec-signup_error_messages
https://railstutorial.jp/chapters/basic_login?version=5.1#sec-rendering_with_a_flash_message

[Rails]ログイン、ログアウト機能
https://railstutorial.jp/chapters/basic_login?version=5.1#cha-basic_login
→外部ライブラリ- devise
https://qiita.com/cigalecigales/items/f4274088f20832252374

[Rails]ログインしてブラウザを閉じてもログイン状態を維持したい! - Remember me
https://railstutorial.jp/chapters/advanced_login?version=5.1#sec-remember_me

[Rails]サムネイル画像を入れたい
https://railstutorial.jp/chapters/sign_up?version=5.1#sec-a_gravatar_image

[Rails]編集画面を表示したり更新したり削除したりしたい
https://railstutorial.jp/chapters/advanced_login?version=5.1#sec-remember_me

[Rails]入力する値が期待してるものと違ったら元に戻したい
期待してるものかどうかを判別→正規表現、バリデーション
https://railstutorial.jp/chapters/modeling_users?version=5.1#sec-user_validations
元に戻す→リダイレクト
https://railstutorial.jp/chapters/sign_up?version=5.1#sec-unsuccessful_signups

[Rails]いいね機能
↓これでできた↓
https://qiita.com/jaramon/items/248bcb4b56e9fed8fc90
https://qiita.com/krppppp/items/a3e6264f0458d80ca5b7
https://qiita.com/mochikichi321/items/259381d359222b78eca8
https://qiita.com/kenny_0418/items/4bccf641804763306722
https://qiita.com/fuku_tech/items/c9835b7d90bd5effe8ad
https://qiita.com/hayabusa3703/items/2b916e652a1dc85bb6e3
いいね機能の文献
https://qiita.com/rainbow___0/items/a7d66755175b3e8a9636
https://qiita.com/YuitoSato/items/94913d6a349a530b2ea2
https://qiita.com/nojinoji/items/2c66499848d882c31ffa

[Rails]アカウントIDを乱数で表示したい(アカウント名 URL rails)
https://qiita.com/awakia/items/c2c790dc51e5b084af10
https://teratail.com/questions/1442
https://blog.takady.net/blog/2015/11/29/rails-routing-with-username-instead-of-id/
https://teratail.com/questions/1442
https://stackoverrun.com/ja/q/1267118
http://vdeep.net/rubyonrails-friendly-id
https://blog.takady.net/blog/2015/11/29/rails-routing-with-username-instead-of-id/

[Rails]多階層カテゴリを作る。ancestry
https://qiita.com/Sotq_17/items/120256209993fb05ebac
https://qiita.com/k_suke_ja/items/aee192b5174402b6e8ca

検索
https://qiita.com/sconeman/items/fca9efa22536b8b2a9ef
https://qiita.com/sconeman/items/6c839c40371b4ec213bf

リプライ
https://qiita.com/krppppp/items/7f9563e08150a008592f
https://qiita.com/sconeman/items/f701474d2c0276b52bfb
https://qiita.com/johnslith/items/8d931b66f72c97c78199
https://qiita.com/EastResident/items/7c96a69be16f5f46c79a
https://iberiko665.hatenablog.com/entry/2018/12/06/031005
https://qiita.com/rockguitar67/items/4fd28cba0c243a8d0ba5
https://qiita.com/kurawo___D/items/d2fefdd329f5310113aa

リプライのモーダルフォーム(RailsTutorial拡張)
https://github.com/kotaroooo0/sample-app
https://www.techry.net/blogs/1271
https://qiita.com/sofpyon/items/1e72426598eed745f031

リプライをツリー、スレッドにして表示
http://railscasts.com/episodes/262-trees-with-ancestry?language=ja&view=asciicast
https://stackoverflow.com/questions/51326115/reply-a-comment-not-working-using-ancestry-gem-in-rails
https://www.youtube.com/watch?v=I45gHY522Ms

リプライでリプライ名を対象ユーザーページへのリンクとして表示したい
https://iberiko665.hatenablog.com/entry/2018/12/06/031005

オートコンプリート機能
https://qiita.com/swamp09/items/6fb852489b4dc3acfbdd

動画アップロード
http://ken992.hatenablog.com/entry/2018/07/04/202616
https://dev.classmethod.jp/articles/ruby-on-rails-carrierwave-ffmpeg/

アカウントID(1日)gemがあるらしい
https://qiita.com/awakia/items/c2c790dc51e5b084af10
https://teratail.com/questions/1442
https://blog.takady.net/blog/2015/11/29/rails-routing-with-username-instead-of-id/
https://teratail.com/questions/1442
https://stackoverrun.com/ja/q/1267118
http://vdeep.net/rubyonrails-friendly-id
https://blog.takady.net/blog/2015/11/29/rails-routing-with-username-instead-of-id/

タグ機能
自力
https://qiita.com/tobita0000/items/daaf015fb98fb918b6b8
https://qiita.com/you8/items/b2394104c6f9865f5d46
gem
https://qiita.com/YUD96/items/f751010f8a58efaef6ed
https://qiita.com/ddd555/items/e1caa8b73d118822a0a2

通知機能
https://qiita.com/nekojoker/items/80448944ec9aaae48d0a
https://qiita.com/tktk0430/items/bdb8fbcf4ce3258b2d41
https://qiita.com/yuto_1014/items/2db1dd4fcd7945b980f7

トレンド

管理画面
https://qiita.com/enomotodev/items/5f6d9348207124a41bf9

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

[Rails] CircleCIでRubocop・RSpecを実施しCapistranoを走らせAWS(EC2)に自動でプロイする方法

はじめに

CircleCIをポートフォリオに組み込みデプロイの自動化はしたものの、テストの自動化は行なっていなかったので、そちらの実装ができたので、記事にしていこうと思います。

前提

  • テストを実装済み(RSpec)
  • Rubocop導入済み
  • EC2にデプロイ済み
  • 開発環境にDockerを用いている

開発環境

  • Ruby 2.6.5
  • Rails6
  • MySQL 5.6.50
  • Docker
  • docker-compose
  • Capistrano(自動デプロイツール)

完成コード

.circleci/config.yml
version: 2.1

orbs:
  ruby: circleci/ruby@1.1.0

jobs:
  build:
    docker:
      - image: circleci/ruby:2.6.5-node-browsers
        environment:
          BUNDLER_VERSION: 2.1.4
    steps:
      - checkout
      - ruby/install-deps

  test:
    parallelism: 3
    docker:
      - image: circleci/ruby:2.6.5-node-browsers
        environment:
          DB_HOST: 127.0.0.1
          RAILS_ENV: test
          BUNDLER_VERSION: 2.1.4
      - image: circleci/mysql:8.0
        command: --default-authentication-plugin=mysql_native_password
        environment:
          MYSQL_ALLOW_EMPTY_PASSWORD: "true"
          MYSQL_ROOT_HOST: "%"

    steps:
      - checkout
      - ruby/install-deps
      - run: yarn install
      - run: mv config/database.yml.ci config/database.yml
      - run:
          name: Wait for DB
          command: dockerize -wait tcp://localhost:3306 -timeout 1m
      - run: bundle exec rake db:create
      - run: bundle exec rake db:schema:load
      - run:
          name: RuboCop
          command: bundle exec rubocop
      - run: bundle check --path=vendor/bundle || bundle install --path=vendor/bundle --jobs=4 --retry=3
      - run: mkdir ~/rspec
      - run:
          command: bundle exec rspec --format progress --format RspecJunitFormatter -o ~/rspec/rspec.xml
          when: always
      - store_test_results:
          path: ~/rspec

  deploy:
    docker:
      - image: circleci/ruby:2.6.5-node-browsers
        environment:
          BUNDLER_VERSION: 2.1.4
    steps:
      - checkout
      - ruby/install-deps
      - add_ssh_keys:
          fingerprints: "xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx"
      - deploy:
          name: Capistrano deploy
          command: bundle exec cap production deploy

workflows:
  version: 2
  build_accept_deploy:
    jobs:
      - build
      - test:
          requires:
            - build
      - deploy:
          requires:
            - test
          filters:
            branches:
              only: master


解説をすると、環境を指定するところではtest環境を指定します。
そして、DBもtest環境に合わせるためconfigディレクトリdatabase.yml.ciファイルを作成します。

作成したら以下のように編集しましょう。

config/database.yml.ci
test:
  adapter: mysql2
  encoding: utf8
  pool: 5
  username: 'root'
  port: 3306
  host: '127.0.0.1'
  database: ci_test

このようにtest環境用のDBを設定できればCircleCIでの環境構築は終了です。

ここからは実際にテストをするための準備に取り掛かります。コードでいうとtest(testジョブ)の部分に当たります。
主に公式を参考にして作業しましたので、公式も参考にしてください。

CircleCI 公式ドキュメント 言語ガイド: Ruby

また、その際にCircleCI用にRSpecのrspec_junit_formatterというGemをインストールする必要があります。
こちらも公式に書いてあります。

CircleCI 公式ドキュメント テスト メタデータの収集

Gemfile
group :test do
  # Adds support for Capybara system testing and selenium driver
  gem 'capybara', '>= 2.15'
  gem 'selenium-webdriver'
  # Easy installation and use of web drivers to run system tests with browsers

 # rspec_junit_formatterをgroup :test do内に記述
  gem 'rspec_junit_formatter'
  gem 'webdrivers'
end

忘れずにbundle installをしてサーバーの再起動を行いましょう。

これで準備が整いました。
testの部分を見ていきましょう。

testジョブ

.circleci/config.yml
test:
  parallelism: 3
    docker:
      - image: circleci/ruby:2.6.5-node-browsers
        environment:
          DB_HOST: 127.0.0.1
          RAILS_ENV: test
          BUNDLER_VERSION: 2.1.4
      - image: circleci/mysql:8.0
        command: --default-authentication-plugin=mysql_native_password
        environment:
          MYSQL_ALLOW_EMPTY_PASSWORD: "true"
          MYSQL_ROOT_HOST: "%"

    steps:
      - checkout
      - ruby/install-deps
      - run: yarn install
      - run: mv config/database.yml.ci config/database.yml
      - run:
          name: Wait for DB
          command: dockerize -wait tcp://localhost:3306 -timeout 1m
      - run: bundle exec rake db:create
      - run: bundle exec rake db:schema:load
      - run:
          name: RuboCop
          command: bundle exec rubocop
      - run: bundle check --path=vendor/bundle || bundle install --path=vendor/bundle --jobs=4 --retry=3
      - run: mkdir ~/rspec
      - run:
          command: bundle exec rspec --format progress --format RspecJunitFormatter -o ~/rspec/rspec.xml
          when: always
      - store_test_results:
          path: ~/rspec

この記述でテストを実行することができます。
ただ、自分の場合はテスト時に以下のエラーが出ました。

Webpacker::Manifest::MissingEntryErrorというエラーが出て、コンパイルができないとRSpecさんに怒られてしまいました。

ということでstepsyarn installコマンドを実行するように追記しました。

.circleci/config.yml
   steps:
      - checkout
      - ruby/install-deps
      - run: yarn install
      - run: mv config/database.yml.ci config/database.yml
      - run:
          name: Wait for DB
          command: dockerize -wait tcp://localhost:3306 -timeout 1m
      - run: bundle exec rake db:create
      - run: bundle exec rake db:schema:load
      - run:
          name: RuboCop
          command: bundle exec rubocop
      - run: bundle check --path=vendor/bundle || bundle install --path=vendor/bundle --jobs=4 --retry=3
      - run: mkdir ~/rspec
      - run:
          command: bundle exec rspec --format progress --format RspecJunitFormatter -o ~/rspec/rspec.xml
          when: always
      - store_test_results:
          path: ~/rspec

これでテストが上手く通れば、デプロイが自動で行われます。
成功するとGUI上でこのように表示されます。

d68ba1f5716b6185acde3f68447f6009.png

ちなみに、このテスト実行時、Rubocopでのエラーが起こることがあるようです。

その場合はrubocop.ymlに記述を加えます。

rubocop.yml
AllCops:
  TargetRubyVersion: # 自分のRubyのバージョン

このように記述するとエラーが解決できるようです。
詳しくは下記の記事を参考にしてみてください。

プッシュ時にRSpec+Rubocopを自動でするようにCircleCIでbuildする【Rails】

ちなみに、.circleci/config.ymlの記述などは以下の記事も非常にわかりやすいので参考にしてみてください。

CircleCI + Capistrano + AWS(EC2) + Railsで自動デプロイしてみた

ということで、残すはデプロイということなので、そこも見ていきましょう。

deployジョブ

.circleci/config.yml
deploy:
    docker:
      # 自分のRubyのバージョンを指定
      - image: circleci/ruby:2.6.5-node-browsers
        environment:
      # ローカルでインストールされているbundlerのバージョンを指定
          BUNDLER_VERSION: 2.1.4
    steps:
      - checkout
      - ruby/install-deps
      - add_ssh_keys:
          fingerprints: "xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx"
      - deploy:
          name: Capistrano deploy
          command: bundle exec cap production deploy

この辺は僕も記事にしているのでそちらのほうが参考になるかなと思います。ここはほぼ公式どおりなのでそちらを読み進めてもらったほうがいいかなと、下記を参考にしてみてください。

Workflows

.circleci/config.yml
workflows:
  version: 2
  build_accept_deploy:
    jobs:
      - build
      - test:
          requires:
            - build
      - deploy:
          requires:
            - test
          filters:
            branches:
              only: master

Gitのmasterブランチにpush・mergeされるたびにCircleCIが走って、CI/CDしてくれるようルールを決めているような感じです。

以上で、RailsとAWSのCI/CDの実装は完了です。
参考になれば幸いです。

参考文献

CircleCI 公式ドキュメント 言語ガイド: Ruby

CircleCI 公式ドキュメント テスト メタデータの収集

公式ドキュメント デプロイの構成

初心者向け! Rails6+CircleCI+Capistrano+AWS(EC2)で自動デプロイ

CircleCI + Capistrano + AWS(EC2) + Railsで自動デプロイしてみた

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

axios では xhr? が効かない

自分用メモ。

axiosはAjaxで通信するときに、リクエストヘッダーにX-Requested-Withを付けません。

参照: https://github.com/axios/axios/issues/1322

なので、Railsのxhr?メソッドは偽になります。

if request.xhr?
 # axiosだと効かない
end

xhr?メソッドを使いたければ、JavaScript側でaxiosのヘッダーにX-Requested-Withを追加する設定が必要です。

Axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';

あるいは、Rails側でX-Requested-Withがなくても動くようにします。

if params[:format] == 'json'
 # Ajaxの場合の処理
end
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Railsによるfollow機能の実装

はじめに

Railsを使ってfollow機能を実装する中で気づいたことなどを自分用のメモとして手順と共に残しておこうと思い記事を書きました。間違っているところなどありましたらご指摘いただけると助かります!

Follow機能実装にあたって行う手順

  1. Followモデルを作り、dbにfollowsテーブルを追加
  2. 各モデルで関連付けを実装、フォロワー・フォローしている人を簡単に取得できるメソッドを定義する。
  3. 各controller, viewでそれらを表示

おおまかにはこんな流れです。基本的にはRailsチュートリアル第4版を参考にしています。

Followsモデルを追加

terminal
bin/rails g model Follow follower_id:integer followed_id:integer

上のように、rails g のコマンドでモデルを作ります。今回は、あるユーザーから見て、フォローされている人のidを格納するfollower_idカラム、そのユーザー自身のidを格納するfollowed_idカラム、を定義しました。
そうすると、migrationファイルが生成されるので、

terminal
bin/rails db:migrate

を実行します。これでデータベースへの反映は完了です。

各モデルでの関連付けの実装

今回は、Followモデルには User モデルのidのみを格納するので、Userモデルとの関連付けのみを行います。下のコードは、あるユーザーがフォローしているユーザーを取得するために行う関連付けの例です。

User.rb
has_many :active_follows, class_name: "Follow",
                          foreign_key: "follower_id",
                          dependent: :destroy
has_many :following, through: :active_follows, source: :followed
Follow.rb
belongs_to :follower, class_name: "User"

関連付けについては以下のような規則で行います。
1. model同士の関係性を1対1なのか、1対多なのか考える。
2. 1対1の場合はbelongs_to, 1対多の場合はhas_many, で関連付けする。

今回であれば、Userモデルから見た場合、Followという行為は何回でも行えるものなので、1ユーザーに対して多数のFollowの情報が存在します。よって、1対多の関係です。
反対に、Followモデルから見た場合、一つのFollowレコードに対して、フォローしているユーザーは一人、フォローされているユーザーも一人です。よって、1対1の関係です。(ただし、Followモデルの中のfollower_idが取得したいのか、followed_idが取得したいのか、ということは関連付けを一つ行っただけではわかりません。なので、今回はフォローする側、される側、の二つに分けて関連付けを行います。初め僕はここがよくわからなかったので注釈としておきます)
また、関連付けでは、次の項目を指定することができます。

  1. 関連付けをするメソッドの名前
  2. 関連づけるクラスの名前
  3. 参照する外部キーの名前
  4. 関連付け先のモデルとの依存関係

これは、以下のように対応しています。

User.rb
has_many :active_follows, #ここ1
      class_name: "Follow", #ここ2
      foreign_key: "follower_id" #ここ3
      dependent: :destroy #ここ4

1で、Userモデルのインスタンスに対して使う関連付け名(要するにメソッド名)を定義します。
2で、そのメソッドを用いたときどのクラスを参照するか指定します。今回はFollowモデルとの関連付けなのでFollowを指定しています。
3で、Followモデルのどのカラムを参照するか指定します。今回はあるユーザーがフォローしているユーザーを取得したいので、外部キーとしてfollower_idを指定します。
4で、そのモデルのインスタンスが削除された場合、紐づいているモデルの対応するカラムも削除するかどうかを指定します。ユーザーを削除した場合、フォロー情報も消去したいのでdependent: :destroyを指定します。

ここまでUserモデル側での関連付けについて説明しましたが、Followモデル側では次のコードのみで済みます。

Follow.rb
belongs_to : follower, class_name: "User"

これは、Railsの自動的な推測によります。こちらでは、1と2の指定のみで、誰がフォローしているかの情報をfollowerというメソッドで取り出せるようになります。class名を指定すると、そのクラスの主キーがfollower_idと一致するユーザーを取得してくれます。

最後に、今のままではUserインスタンスに対してactive_followsを用いてもFollowテーブルの情報が返ってくるのみです。フォローしているユーザーまで返してくれると便利なので、following関連付けを追加します。それが以下の部分です。

User.rb
has_many :following, through: active_follows, source: :followed

これは、followingという名前の関連付け(メソッド)を実行すれば、active_followsというメソッドを実行して、さらにそれにより得られたFollowテーブルのデータたちのfollowed_id というカラムのidを持つユーザーを取得してね、という意味になります。sourceに指定したfollowedの _id の部分はRailsによって補完されます。

以上で、Userモデルのインスタンスからフォロワーを取得してくる関連付けが定義できました!長々とわかりづらく書いてしまい申し訳ありません・・・

各controller,viewでの表示

これは他のところでも用いている方法と全く同じなので割愛します。取得した@userなどのインスタンス変数に対して先ほど定義したfollowingメソッドを実行し、レイアウトを整えるのみです。

終わりに

チュートリアル1周目にはあまり理解できていなかった部分を、ポートフォリオを作っている最中にしっくりきたので言葉に起こしておきました。自分なりの理解なのでわかりづらいところ、間違っているところなどあると思います。参考にされる際はその点御留意くださりますと幸いです。

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

RSpecでpry-byebug(binding.pry)が使えなかった時の対応(初歩)

環境

Mac OS X
Ruby: 2.7.1
Rails: 6.0.3.4

状況

RSpecでテスト書いてるときに、hogehoge.errors[:hoge]の中身ってどうなってるのか気になることがあった。
そこでpry-byebugを使って確認しようと、テストコードの該当箇所にbinding.pryを入れてキャッチしようとしたが、キャッチできずにそのままテストが進んでしまった。

原因

pry-byebugのgemがdevelopmentにしか入っていなかったから。

Gemfile
group :development do

 gem 'pry-byebug'

end

対応

pry-byebugのgemをtest環境に入れる(自分の場合はdevelopment環境でも使いたいので以下の場所にいれた)

Gemfile
group :development, :test do

 gem 'pry-byebug'

end

結果

該当箇所で処理がとまり、pry-byebugが使えるようになった!

感想

開発環境と本番環境の違いはherokuなどを使っていたのでわかってたのですが、開発環境とテスト環境で違うという感覚が無かったので勉強になりました...

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

[Rails] GoogleMapを使って、マーカーが立っている位置をDBに保存する!

前提

・Railsのアプリケーションであること
・記事作成機能が存在すること
・GoogleMapを自分のアプリケーション上で表示することができていること
※下記記事などが非常に参考になると思います。

https://qiita.com/tiara/items/4a1c98418917a0e74cbb#%E5%9C%B0%E5%90%8D%E3%81%A7map%E3%82%92%E7%A7%BB%E5%8B%95%E3%81%99%E3%82%8B

https://qiita.com/nagaseToya/items/e49977efb686ed05eadb

目標

記事作成の際、マップにマーカーを1つだけ立てられるようにし、その位置情報をDBに保存すること。
マーカーの立て方としては以下を想定。

・マップをクリック
・位置を検索

それではやっていきましょう!

gemのインストール ※いらない方はスキップしてください

Gemfile
gem 'geocoder'
gem 'gon'

※この記事においてはこの2つのgemは使用しません。しかし、これらのgemが入っているという前提で進めていきます。

geocoderは便利なメソッドが豊富です。
RailsでGoogleMapを使用のであれば入れておいたほうが良いです。
gonはController内でセットした変数をJavascript内で使う事ができます。
DBに保存した位置情報を記事詳細画面で表示させる際に非常に便利です。

geocoder
https://github.com/alexreisner/geocoder

gon
https://github.com/gazay/gon

これらを記述した後、bundle install します。

マップの情報保存用テーブル作成

指定した緯度、経度を保存するテーブルを作成します。
まず、マイグレーションファイルを作成しましょう。
マイグレーションファイルはこんな感じにします。
私の場合は、記事と地図を関連付けさせたいのでこのようになっています。
マップ単品の方は
t.references :article, null: false, foreign_key: true
の部分はいらないかと思います。

timestamp_xxx.rb(マイグレーションファイル)
class CreateMaps < ActiveRecord::Migration[6.0]
  def change
    create_table :maps do |t|

    t.references :article, null: false, foreign_key: true
    t.string :address
    t.float :latitude
    t.float :longitude

    t.timestamps
    end
  end
end

その後、rails db:migrate します。

モデルの関連付け

関連付けさせなくて良い方はスキップしても大丈夫です。

map.rb
class Map < ApplicationRecord
  belongs_to :article
  geocoded_by :address
  after_validation :geocode
end
article.rb
has_one :map, dependent: :destroy

マップの表示

表示だけならこれだけでできると思います。

〇〇.html.erb
<div id='map'></div>   
<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?key=<%= ENV['GOOGLE_MAP_API'] %>&callback=initMap" async defer></script>   
<script>
let map

function initMap (){
  myLatLng = {lat: 35.68090045006303, lng: 139.76690798417752}
  map = new google.maps.Map(document.getElementById('map'), {
  center: myLatLng,
  zoom: 12
  });
</script>

マップのクリックしたところにマーカーを立てることができるようにする

記事作成と同時にクリックした箇所の経度、緯度を保存できるようにします。
また、マーカを削除できるボタンも追加しました。

_article_form.html.erb
<div id="article_form" class="field">
  <%= f.label :title, "タイトル" %>
  <%= f.text_area :title %>
  マップ
  <div id='map'></div>
  <%= f.fields_for :map, @article.build_map do |map| %>   
    <%= map.hidden_field :latitude %>
    <%= map.hidden_field :longitude %>
  <% end %>
  <%= f.label :content, "本文" %>
  <%= f.rich_text_area :content,placeholder:"入力してください" %>
  <%= f.label :tag_list %>
  <%= f.file_field :image, accept: "image/jpeg,image/gif,image/png" %>
</div>
<div id="article_button"><%= f.submit "投稿!",class:"btn"%></div>
<% end %>
<button id="del" class="btn" onclick="deleteMarker();">マーカー削除</button>


<script>

var marker
var myLatLng
var map
var map_lat
var map_lng

function initMap(){
   myLatLng = {lat: 35.68090045006303, lng: 139.76690798417752}
   marker = new google.maps.Marker();
   map_lat = document.getElementById('article_map_latitude')
   map_lng = document.getElementById('article_map_longitude')
   map = new google.maps.Map(document.getElementById('map'), {
  center: myLatLng,
  zoom: 8
  });


 google.maps.event.addListener(map, 'click', mylistener);

    //クリックしたときの処理
  function mylistener(event){
    //markerの位置を設定
    //event.latLng.lat()でクリックしたところの緯度を取得
    marker.setPosition(new google.maps.LatLng(event.latLng.lat(), event.latLng.lng()));
    //marker設置
    marker.setMap(map);    
    map_lat.value = event.latLng.lat();
    map_lng.value = event.latLng.lng();

  }
}
function deleteMarker(){
  marker.setMap(null);
  map_lat.value = "";
  map_lng.value = "";
}



</script>
<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?key=<%= ENV['GOOGLE_MAP_API'] %>&callback=initMap" async defer></script> 
 30 

下記のコードによってarticleと関連付けたmapにデータを渡すことができます。

<%= f.fields_for :map, @article.build_map do |map| %>
<%= map.hidden_field :latitude %>
<%= map.hidden_field :longitude %>
<% end %>

また、これらに値を入れるコードはこの部分です。

map_lat = document.getElementById('article_map_latitude')
map_lng = document.getElementById('article_map_longitude')
//↑map.hidden_fieldによって生成された<input>のidです。
//人によって違うかもしれないのでブラウザの開発者モードで確認してください。

//〜〜省略〜〜

map_lat.value = event.latLng.lat();
map_lng.value = event.latLng.lng();

そして、値をコントローラに渡すため、StrongParameterの設定も必要です。
binding.pryなどで、どのようなparamsが渡ってきているか確認しておいたほうが良いかと思います。

article_controller.rb
    def article_params
      params.require(:article)
            .permit(:content, :title,
             map_attributes: [:id, :latitude, :longitude])
    end

これを保存するコードはこちらです。

article_controller.rb
def create
  @article = current_user.articles.build(article_params)
  if @article.save
    latitude = params[:article][:map][:latitude]
    longitude = params[:article][:map][:longitude]
    unless latitude.empty && longitude.empty?
      @map = @article.build_map(
        latitude: latitude,
        longitude: longitude
      )
      @map.save
    end
#〜〜〜〜〜〜省略〜〜〜〜〜〜〜〜〜〜〜
end

位置検索でもマーカーを立てることができるようにする

この場合、GoogleMapAPIだけでなく、Geocoding APIも必要になってきます。
紆余曲折あり、最終的に完成したのがこちらです。

<div class="row">
  <div class="col s12 m10 push-m1">
    <div class="card">
      <div class="card-content article-card">        
        <%= form_with(model:@article,local: true,class:"margin-30") do |f|%>
        <%= render 'shared/error_messages',object: f.object %>        
          <div id="article_form" class="field">
            <div class="input-field">              
              <%= f.text_field :title,class: "materialize-textarea" %>
              <%= f.label :title, "タイトル" %>
            </div>
            <div class="input-field">              
              <%= f.text_field :tag_list, value: @article.tag_list.join(','), class: "materialize-textarea" %>
              <%= f.label :tag_list,"タグ ※コンマ区切りで記入してください" %>
            </div>

        <!-- 追加したイベント -->    

            <div class="input-field">
              <input type="text" id="address" placeholder="地名、施設名などを入力するか、地図をクリックしてマーカーを立ててください">
              <label>場所</label>
              <a class="btn" onclick="codeAddress()">地図検索</a>
              <a id="del" class="btn marker-delete right" onclick="deleteMarker()">
              <i class="fas fa-map-marker-alt fas-2x" style="color:red"></i>削除
              </a>
            </div>

        <!-- ここまで -->

            <div id='map' class="margin-t-b-14"></div>          
            <%= f.fields_for :map, @article.build_map do |map| %>   
            <%= map.hidden_field :latitude %>
            <%= map.hidden_field :longitude %>
            <% end %>            
              <%= f.rich_text_area :content,placeholder:"入力してください",class: "materialize-textarea" %>
              <%= f.label :content, "本文" %>
            <%= f.label :thumbnail, "サムネイル" %>
            <%= f.file_field :thumbnail, accept: "image/jpeg,image/gif,image/png",class:"file_field" %>
            <div id="article_button"><%= f.submit "投稿!",class:"btn col s6 m6 article-form-btn push-s3 push-m3"%></div>
          </div>
        <% end %> 
      </div>
    </div>
  </div>
</div>





<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?key=<%= ENV['GOOGLE_MAP_API'] %>&callback=initMap" async defer></script>
<script>
var marker
var myLatLng
var map_lat
var map_lng
var geocoder
var map_result
let map

function initMap (){
  myLatLng = {lat: 35.68090045006303, lng: 139.76690798417752}
  map_lat = document.getElementById('article_map_latitude')
  map_lng = document.getElementById('article_map_longitude')
  marker = new google.maps.Marker();

  map = new google.maps.Map(document.getElementById('map'), {
  center: myLatLng,
  zoom: 12
  });

google.maps.event.addListener(map, 'click', mylistener);

     //クリックしたときの処理
function mylistener(event){
    marker.setPosition(new google.maps.LatLng(event.latLng.lat(), event.latLng.lng()));
    marker.setMap(map);    
    console.log(event.latLng.lat(), event.latLng.lng());
    map_lat.value = event.latLng.lat();
    map_lng.value = event.latLng.lng();
  }
}

  function deleteMarker(){
    marker.setMap(null);
    map_lat.value = "";
    map_lng.value = "";
}

//追加した関数

function codeAddress(){
  geocoder = new google.maps.Geocoder()
  let inputAddress = document.getElementById('address').value;
  geocoder.geocode( { 
    'address': inputAddress,
     'region': 'jp'
    }, function(results, status) {
    if (status == 'OK') {
      map.setCenter(results[0].geometry.location);
      map_result = results[0].geometry.location;
      map_lat.value = map_result.lat();
      map_lng.value = map_result.lng();
      marker.setPosition(new google.maps.LatLng(map_result.lat(), map_result.lng()));
      marker.setMap(map);
      console.log(map_lat.value,map_lng.value);
    } else {
      alert('該当する結果がありませんでした');
      initMap();
    }
  });   
}
</script>

随分長いコードになってしまいました。
しかし、実際は関数とイベントが1つ増えただけなのでその部分を見てもらえればわかるかと思います!
これによって、

・地図をクリック
・位置を検索
することで地図上にマーカーを立て、その位置情報をDBに保存することができるようになりました!
これらを利用する方法についてはまた後日記事を書こうかと思います。

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

[Rails6] GoogleMapを使って、マーカーが立っている位置をDBに保存する!

前提

・Railsのアプリケーションであること
※私のRailsのバージョンは6.0.3です
・記事作成機能が存在すること
・GoogleMapを自分のアプリケーション上で表示することができていること
※下記記事などが非常に参考になると思います。

https://qiita.com/tiara/items/4a1c98418917a0e74cbb#%E5%9C%B0%E5%90%8D%E3%81%A7map%E3%82%92%E7%A7%BB%E5%8B%95%E3%81%99%E3%82%8B

https://qiita.com/nagaseToya/items/e49977efb686ed05eadb

目標

記事作成の際、マップにマーカーを1つだけ立てられるようにし、その位置情報をDBに保存すること。
マーカーの立て方としては以下を想定。

・マップをクリック
・位置を検索(GeocodingAPIを使用)

それではやっていきましょう!

gemのインストール ※いらない方はスキップしてください

Gemfile
gem 'geocoder'
gem 'gon'

※この記事においてはこの2つのgemは使用しません(次の記事で使用予定)。しかし、これらのgemが入っているという前提で進めていきます。

geocoderは便利なメソッドが豊富です。
RailsでGoogleMapを使用のであれば入れておいたほうが良いです。
gonはController内でセットした変数をJavascript内で使う事ができます。
DBに保存した位置情報を記事詳細画面で表示させる際に非常に便利です。

geocoder
https://github.com/alexreisner/geocoder

gon
https://github.com/gazay/gon

これらを記述した後、bundle install します。

マップの情報保存用テーブル作成

指定した緯度、経度を保存するテーブルを作成します。
まず、マイグレーションファイルを作成しましょう。
私の場合は、記事と地図を関連付けさせたいのでこのようになっています。
マップ単品の方は
t.references :article, null: false, foreign_key: true
の部分はいらないかと思います。

timestamp_xxx.rb(マイグレーションファイル)
class CreateMaps < ActiveRecord::Migration[6.0]
  def change
    create_table :maps do |t|

    t.references :article, null: false, foreign_key: true
    t.string :address
    t.float :latitude
    t.float :longitude

    t.timestamps
    end
  end
end

その後、rails db:migrate します。

モデルの関連付け

関連付けさせなくて良い方はスキップしても大丈夫です。

map.rb
class Map < ApplicationRecord
  belongs_to :article
  geocoded_by :address
  after_validation :geocode
end
article.rb
has_one :map, dependent: :destroy

マップの表示

表示だけならこれだけでできると思います。

〇〇.html.erb
<div id='map'></div>   
<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?key=<%= ENV['GOOGLE_MAP_API'] %>&callback=initMap" async defer></script>   
<script>
let map

function initMap (){
  myLatLng = {lat: 35.68090045006303, lng: 139.76690798417752}
  map = new google.maps.Map(document.getElementById('map'), {
  center: myLatLng,
  zoom: 12
  });
</script>

マップ上でクリックしたところにマーカーを立てることができるようにする

記事作成の際に、マップ上でクリックした箇所にマーカーを立てられるようにします。
また、マーカーが立っている箇所の経度、緯度を保存できるようにします。
ついでにマーカーを削除できるボタンも追加しました。

_article_form.html.erb
<div id="article_form" class="field">
  <%= f.label :title, "タイトル" %>
  <%= f.text_area :title %>
  マップ
  <div id='map'></div>
  <%= f.fields_for :map, @article.build_map do |map| %>   
    <%= map.hidden_field :latitude %>
    <%= map.hidden_field :longitude %>
  <% end %>
  <%= f.label :content, "本文" %>
  <%= f.rich_text_area :content,placeholder:"入力してください" %>
  <%= f.label :tag_list %>
  <%= f.file_field :image, accept: "image/jpeg,image/gif,image/png" %>
</div>
<div id="article_button"><%= f.submit "投稿!",class:"btn"%></div>
<% end %>
<button id="del" class="btn" onclick="deleteMarker();">マーカー削除</button>

<script>
var marker
var myLatLng
var map
var map_lat
var map_lng

function initMap(){
   myLatLng = {lat: 35.68090045006303, lng: 139.76690798417752}
   marker = new google.maps.Marker();
   map_lat = document.getElementById('article_map_latitude')
   map_lng = document.getElementById('article_map_longitude')
   map = new google.maps.Map(document.getElementById('map'), {
  center: myLatLng,
  zoom: 8
  });

 google.maps.event.addListener(map, 'click', mylistener);

    //クリックしたときの処理
  function mylistener(event){
    //markerの位置を設定
    //event.latLng.lat()でクリックしたところの緯度を取得
    marker.setPosition(new google.maps.LatLng(event.latLng.lat(), event.latLng.lng()));
    //marker設置
    marker.setMap(map);    
    map_lat.value = event.latLng.lat();
    map_lng.value = event.latLng.lng();
  }
}
function deleteMarker(){
  marker.setMap(null);
  map_lat.value = "";
  map_lng.value = "";
}



</script>
<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?key=<%= ENV['GOOGLE_MAP_API'] %>&callback=initMap" async defer></script> 
 30 

下記のコードによってarticleと関連付けたmapにデータを渡すことができます。

<%= f.fields_for :map, @article.build_map do |map| %>
<%= map.hidden_field :latitude %>
<%= map.hidden_field :longitude %>
<% end %>

また、これらに値を入れるコードはこの部分です。

map_lat = document.getElementById('article_map_latitude')
map_lng = document.getElementById('article_map_longitude')
//↑map.hidden_fieldによって生成された<input>のidです。
//人によって違うかもしれないのでブラウザの開発者モードで確認してください。

//〜〜省略〜〜

map_lat.value = event.latLng.lat();
map_lng.value = event.latLng.lng();

そして、値をコントローラに渡すため、StrongParameterの設定も必要です。
binding.pryなどで、どのようなparamsが渡ってきているか確認しておいたほうが良いかと思います。

article_controller.rb
    def article_params
      params.require(:article)
            .permit(:content, :title,
             map_attributes: [:id, :latitude, :longitude])
    end

これを保存するコードはこちらです。

article_controller.rb
def create
  @article = current_user.articles.build(article_params)
  if @article.save
    latitude = params[:article][:map][:latitude]
    longitude = params[:article][:map][:longitude]
    unless latitude.empty && longitude.empty?
      @map = @article.build_map(
        latitude: latitude,
        longitude: longitude
      )
      @map.save
    end
#〜〜〜〜〜〜省略〜〜〜〜〜〜〜〜〜〜〜
end

位置検索でもマーカーを立てることができるようにする

この場合、GoogleMapAPIだけでなく、Geocoding APIも必要になってきます。
紆余曲折あり、最終的に完成したのがこちらです。

<div class="row">
  <div class="col s12 m10 push-m1">
    <div class="card">
      <div class="card-content article-card">        
        <%= form_with(model:@article,local: true,class:"margin-30") do |f|%>
        <%= render 'shared/error_messages',object: f.object %>        
          <div id="article_form" class="field">
            <div class="input-field">              
              <%= f.text_field :title,class: "materialize-textarea" %>
              <%= f.label :title, "タイトル" %>
            </div>
            <div class="input-field">              
              <%= f.text_field :tag_list, value: @article.tag_list.join(','), class: "materialize-textarea" %>
              <%= f.label :tag_list,"タグ ※コンマ区切りで記入してください" %>
            </div>

        <!-- 追加したイベント -->    

            <div class="input-field">
              <input type="text" id="address" placeholder="地名、施設名などを入力するか、地図をクリックしてマーカーを立ててください">
              <label>場所</label>
              <a class="btn" onclick="codeAddress()">地図検索</a>
              <a id="del" class="btn marker-delete right" onclick="deleteMarker()">
              <i class="fas fa-map-marker-alt fas-2x" style="color:red"></i>削除
              </a>
            </div>

        <!-- ここまで -->

            <div id='map' class="margin-t-b-14"></div>          
            <%= f.fields_for :map, @article.build_map do |map| %>   
            <%= map.hidden_field :latitude %>
            <%= map.hidden_field :longitude %>
            <% end %>            
              <%= f.rich_text_area :content,placeholder:"入力してください",class: "materialize-textarea" %>
              <%= f.label :content, "本文" %>
            <%= f.label :thumbnail, "サムネイル" %>
            <%= f.file_field :thumbnail, accept: "image/jpeg,image/gif,image/png",class:"file_field" %>
            <div id="article_button"><%= f.submit "投稿!",class:"btn col s6 m6 article-form-btn push-s3 push-m3"%></div>
          </div>
        <% end %> 
      </div>
    </div>
  </div>
</div>

<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?key=<%= ENV['GOOGLE_MAP_API'] %>&callback=initMap" async defer></script>
<script>
var marker
var myLatLng
var map_lat
var map_lng
var geocoder
var map_result
let map

function initMap (){
  myLatLng = {lat: 35.68090045006303, lng: 139.76690798417752}
  map_lat = document.getElementById('article_map_latitude')
  map_lng = document.getElementById('article_map_longitude')
  marker = new google.maps.Marker();

  map = new google.maps.Map(document.getElementById('map'), {
  center: myLatLng,
  zoom: 12
  });

google.maps.event.addListener(map, 'click', mylistener);

     //クリックしたときの処理
function mylistener(event){
    marker.setPosition(new google.maps.LatLng(event.latLng.lat(), event.latLng.lng()));
    marker.setMap(map);    
    console.log(event.latLng.lat(), event.latLng.lng());
    map_lat.value = event.latLng.lat();
    map_lng.value = event.latLng.lng();
  }
}

  function deleteMarker(){
    marker.setMap(null);
    map_lat.value = "";
    map_lng.value = "";
}

//追加した関数

function codeAddress(){
  geocoder = new google.maps.Geocoder()
  let inputAddress = document.getElementById('address').value;
  geocoder.geocode( { 
    'address': inputAddress,
     'region': 'jp'
    }, function(results, status) {
    if (status == 'OK') {
      map.setCenter(results[0].geometry.location);
      map_result = results[0].geometry.location;
      map_lat.value = map_result.lat();
      map_lng.value = map_result.lng();
      marker.setPosition(new google.maps.LatLng(map_result.lat(), map_result.lng()));
      marker.setMap(map);
      console.log(map_lat.value,map_lng.value);
    } else {
      alert('該当する結果がありませんでした');
      initMap();
    }
  });   
}
</script>

随分長いコードになってしまいました。
しかし、実際は関数とイベントが1つ増えただけなのでその部分を見てもらえればわかるかと思います!
これによって、マップをクリックor位置を検索することで地図上にマーカーを立て、その位置情報をDBに保存することができるようになりました!
これらを利用する方法についてはまた後日記事を書こうかと思います。

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

Uncaught Error: Cannot find moduleが出た話

こんにちは。
現在JavaScriptを勉強中です。
今回はUncaught Errorに出くわしたのでその話をしたいと思います。

結論から申し上げますと、すごくしょうもない理由でした(笑)

経緯

Ruby on Rails アプリケーションにでJavaScriptを実装している段階で、すでに作成されているapplication.jsに読み込ませるJavaScriptファイルを記述しました。
ローカルサーバーにて、挙動を確認しようとしたところ、下記のようなエラーが出ました。

application.js:10 Uncaught Error: Cannot find module '../sample.js'
    at webpackMissingModule (application.js:10)
    at Object../app/javascript/packs/application.js (application.js:10)
    at __webpack_require__ (bootstrap:19)
    at bootstrap:83
    at bootstrap:83
webpackMissingModule @ application.js:10
./app/javascript/packs/application.js @ application.js:10
__webpack_require__ @ bootstrap:19
(anonymous) @ bootstrap:83
(anonymous) @ bootstrap:83

仮説

①webpackMissingModuleとあるので、またgem関連でエラーが生じているのかなと思い、bundle installを実施するも改善なし。
②読み込ませたいJavaScriptファイル内の記述がおかしいのかなとも考えましたが、そもそも読み込まないエラーなのであればその線は無い・・・。
③エラー内容をよく見ると、webpackMissingModuleの後に、(application.js:10)とあり、application.jsの10行目がおかしいのでは?と捉えられる・・・。

解消方法

結論としては、読み込ませるJavaScriptファイルの配置ミスでした。

application.jsの10行目を見ると、私はこのように記述をしていました。

require("../sample")

それに対して、実際のsample.jsファイルはこのような配置になっておりました。

app
 javascript
  channels
  packs
   sample.js

ファイル配置の書き方、見にくかったらすみません・・・。

上記のようにpacksのなかにsample.jsが存在しており、かつapplication.js内での読み込ませる記述が("../sample")となっていたため、今回のエラーが発生したようです。

したがって、sample.jsファイルを1つ上位ディレクトリ、javascriptディレクトリ直下に配置し直したところ、エラーは改善されました。

考察

今回私は何も考えずにパスの指定を

require("../sample")

としてしまいました。

このパスの指定における .(ドット)と..(ドットドット)の意味をしっかり把握しておりませんでした。

.(ドット)→現在のディレクトリを表す。今回の場合であれば、application.jsが存在するディレクトリ、packsになります。
..(ドットドット)→現在のディレクトリより1つ上位のディレクトリを表す。今回の場合、application.jsが存在するディレクトリの1つ上位、javascriptディレクトリになります。

パスの指定というのは色々複雑で混乱してしまいますね。
とてもしょうもない理由で発生したエラーでしたが、誰かの参考になればとても幸いです。

ご覧いただきありがとうございました!

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

rails kaminariのページネーションについて

@client.event
async def on_message(message):
"""メンバー募集 (.rect@数字)"""
if message.content.startswith(".rect"):
mcount = int(message.content[6:len(message.content)])
text= "あと{}人 募集中\n"
revmsg = text.format(mcount)
frelist = []
msg = await message.channel.send(revmsg)

    await msg.add_reaction("✅")
    await msg.add_reaction("⬅")
    await msg.add_reaction('✖')

    while len(frelist) < int(message.content[6:len(message.content)]):
        target_reaction = await client.wait_for(message=msg)
        if target_reaction.user != msg.author:
            if target_reaction.reaction.emoji == "✅":
                if target_reaction.user.name in frelist:
                    frelist.remove(target_reaction.user.name)
                    mcount += 1
                    await client.edit_message(msg, text.format(mcount) +
                                                    '\n'.join(frelist))
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SystemSpec 実行時のunable to connect to chromedriverエラー

#はじめに

SystemSpecを実行したところ以下のエラーが発生。解決した手順を残しておきます。

環境

windows10 Home
ruby 2.7.0
Rails 6.0.3.4
ubuntu 18.04


     1.1) Failure/Error: visit new_user_session_path

          Selenium::WebDriver::Error::WebDriverError:
            unable to connect to chromedriver 127.0.0.1:9515
          # ./spec/system/users_spec.rb:9:in `block (2 levels) in <main>'

     1.2) Failure/Error: raise Error::WebDriverError, cannot_connect_error_text

          Selenium::WebDriver::Error::WebDriverError:
            unable to connect to chromedriver 127.0.0.1:9515


#試したこと

ターミナルにGoogle chromeをインストール

下の記事を参考にchromeDriverインストールされているか確認、インストールされていなかったのでインストール。


UbuntuにChromeをインストールする

#インストールされていないことを確認
apt list --installed google*


#google.listの最終行に追加
sudo sh -c 'echo "deb http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list'


#登録された事を確認
ls -l /etc/apt/sources.list.d
>-rw-r--r-- 1 root root  55 Jan 21 09:45 google.list

cat /etc/apt/sources.list.d/google.list
>deb http://dl.google.com/linux/chrome/deb/ stable main


#公開鍵をダウンロードして、更に、公開鍵をapt-keyで登録
sudo wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo apt-key add -


#パッケージリストを最新の状態に
sudo apt update


#インストール
sudo apt-get install google-chrome-stable


別のエラーが発生

capybara-webkitがインストールされていなかったのでインストール

     1.1) Failure/Error: visit new_user_session_path

          Selenium::WebDriver::Error::UnknownError:
            unknown error: Chrome failed to start: exited abnormally.
              (unknown error: DevToolsActivePort file doesn't exist)
              (The process started from chrome location /usr/bin/google-chrome is no longer running, so ChromeDriver is assuming that Chrome has crashed.)
          # #0 0x5595e5ed6199 <unknown>
          # ./spec/system/users_spec.rb:9:in `block (2 levels) in <main>'

     1.2) Failure/Error: Unable to infer file and line number from backtrace

          Selenium::WebDriver::Error::UnknownError:
            unknown error: Chrome failed to start: exited abnormally.
              (unknown error: DevToolsActivePort file doesn't exist)
              (The process started from chrome location /usr/bin/google-chrome is no longer running, so ChromeDriver is assuming that Chrome has crashed.)
          # #0 0x555be43fb199 <unknown>

Gemfileにcapybara-webkitを追記してbundle install

Gemfile
  gem 'capybara-webkit'


capybara-webkitをインストールする際にエラー

Gem::Ext::BuildError: ERROR: Failed to build gem native extension.

    current directory: /home/wayne/.rbenv/versions/2.7.0/lib/ruby/gems/2.7.0/gems/capybara-webkit-1.15.1
/home/wayne/.rbenv/versions/2.7.0/bin/ruby -I /home/wayne/.rbenv/versions/2.7.0/lib/ruby/2.7.0 -r
./siteconf20210121-14230-122xkh.rb extconf.rb
*** extconf.rb failed ***
Could not create Makefile due to some reason, probably lack of necessary
libraries and/or headers.  Check the mkmf.log file for more details.  You may
need configuration options.

Provided configuration options:
        --with-opt-dir
        --without-opt-dir
        --with-opt-include
        --without-opt-include=${opt-dir}/include
        --with-opt-lib
        --without-opt-lib=${opt-dir}/lib
        --with-make-prog
        --without-make-prog
        --srcdir=.
        --curdir
        --ruby=/home/wayne/.rbenv/versions/2.7.0/bin/$(RUBY_BASE_NAME)
        --with-gl-dir
        --without-gl-dir
        --with-gl-include
        --without-gl-include=${gl-dir}/include
        --with-gl-lib
        --without-gl-lib=${gl-dir}/lib
        --with-zlib-dir
        --without-zlib-dir
        --with-zlib-include
        --without-zlib-include=${zlib-dir}/include
        --with-zlib-lib
        --without-zlib-lib=${zlib-dir}/lib
Command 'qmake ' not available



公式サイト参考にして
https://github.com/thoughtbot/capybara-webkit/wiki/Installing-Qt-and-compiling-capybara-webkit

sudo apt-get update
sudo apt-get install g++ qt5-default libqt5webkit5-dev gstreamer1.0-plugins-base gstreamer1.0-tools gstreamer1.0-x

bundle install実行


先ほどと同じエラー

     1.1) Failure/Error: visit new_user_session_path

          Selenium::WebDriver::Error::UnknownError:
            unknown error: Chrome failed to start: exited abnormally.
              (unknown error: DevToolsActivePort file doesn't exist)
              (The process started from chrome location /usr/bin/google-chrome is no longer running, so ChromeDriver is assuming that Chrome has crashed.)
          # #0 0x5653be207199 <unknown>
          # ./spec/system/users_spec.rb:9:in `block (2 levels) in <main>'

     1.2) Failure/Error: Unable to infer file and line number from backtrace

          Selenium::WebDriver::Error::UnknownError:
            unknown error: Chrome failed to start: exited abnormally.
              (unknown error: DevToolsActivePort file doesn't exist)
              (The process started from chrome location /usr/bin/google-chrome is no longer running, so ChromeDriver is assuming that Chrome has crashed.)
          # #0 0x559795ddb199 <unknown>



下の記事を参考にして
Rspecの設定はrails_helper.rbにだけ書けばよい

rails_helper.rb
  # system specでheadless chrome使う
  config.before(:each) do |example|
    if example.metadata[:type] == :system
      driven_by :selenium, using: :headless_chrome, screen_size: [1400, 1400]
    end
  end


rspecを実行

Finished in 1.22 seconds (files took 0.7511 seconds to load)
14 examples, 0 failures

通りました!

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

Railsチュートリアルはじめます

はじめに

2020年12月末からプログラミング学習を始め、2021年1月21日現在で以下の学習を終えました。

  • HTML&CSSコース (#Progate)
  • はじめてのHTML (#ドットインストール)
  • はじめてのCSS (#ドットインストール)
  • JavaScriptコース (#Progate)
  • 詳解JavaScript 基礎文法編 (#ドットインストール)
  • Rubyコース (#Progate)
  • Ruby入門 (#ドットインストール)
  • SQLコース (#Progate)
  • MySQL入門 基礎編 (#ドットインストール)
  • MySQL入門 応用編 (#ドットインストール)
  • はじめてのGitとGitHub (#Udemy)

学習を通じて

今までは日々の学習をTwitterでアウトプットしていましたが、Qiitaでのアウトプットも挑戦しようと思います。
Railsチュートリアルの学習で以下のアウトプットをしていく予定です。

  • できるようになったこと
  • わからない→わかったまでの解決方法
  • 感想や意気込み

また、Qiitaでのアウトプットを通じて、以下のスキルを伸ばしていきたいと考えています。

  • マークダウン方式をできるようになる
  • 140文字以上のアウトプットに慣れる

さいごに

  • もっとこうしたほうがいいよ!
  • ここのエラーはこういう意味だよ!

などなど…お気づきの点がございましたら、コメントいただけると大変励みになります。
どうぞよろしくお願い申し上げます。

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

【Rails チュートリアル】"NoMethodError (undefined method `activation_digest=' for #<User:0x00000003156938>

"NoMethodError (undefined method `activation_digest=' for #User:0x00000003156938

について

何故か本番環境だけで、このエラーがみられた

user.rb

    def activate
      self.update_attribute(:activated,    true)
      self.update_attribute(:activated_at, Time.zone.now)
    end

とself.をつけ忘れてた

何故、ローカルだと問題なかったのか、、、

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