Programming Journal

学習したことの整理用です。

Twitter認証機能の追加

半日以上かかってしまったので、エラー部分について自分用に反省メモφ(・

127.0.0.1:3000 に飛びたいのに何故か/loginに遷移する

Twitterログインボタンを設置しているTOPページに遷移するためのstatic_pages_controller.rbskip_before_action :require_loginしてなかった…

CircleCIでエラー

DBをセットアップできていない・・・

rake aborted!
NoMethodError: undefined method `[]' for nil:NilClass
/home/circleci/repo/config/initializers/sorcery.rb:88:in `block in <main>'

該当箇所をみると、これ

  config.twitter.key = Rails.application.credentials.twitter[:key]

CircleCI側にマスターキーを登録したらOKだった

Rails 6よりサポートされたMulti Environment Credentialsをプロジェクトに導入する

今後の課題

TwitterからEmail取得できない問題
permissionがrejectされる!
とりあえず、email取得は諦めて、usersテーブルのemailカラムにはtwitter_idを詰めてごまかしているので今後対応する

f:id:Study-Diary:20201207141441p:plain
Twitter Developer

CircleCIの導入

CircleCI

githubにpushすると同時にCircleCIが起動し、RSpecテストやrubocopのLintチェックを自動で走らせてくれます。
私はズボラでpush前にrubocopやRSpecを実行するのを忘れがちなので、正しいコードをmergeできるようにCircleCIを導入しました。
詰まりに詰まり、1日かかってしまいました。。とりあえず動いてくれてますが、細かい部分は合っているか謎。
公式のサンプル集からコピーして、Qiita等を参考に切り貼りして、ローカルで実行→エラーを潰していくという流れで進めてしまいました。

環境

config.yml

version: 2.1
orbs:
  ruby: circleci/ruby@0.1.2

jobs:
  build:
    docker:
      - image: circleci/ruby:2.7.2-node-browsers-legacy #rubyのversionに合ったコンテナ
        environment:
          - BUNDLER_VERSION: 2.1.4 # これがないとbundle installできない
          - RAILS_ENV: 'test'
          - DB_HOST: 127.0.0.1
      - image: circleci/mysql:5.7
        environment:
          - MYSQL_ALLOW_EMPTY_PASSWORD: 'true'
          - MYSQL_USER: root
          - MYSQL_DB: ci_test # testのDB名指定
    working_directory: ~/repo
    steps:
      # CI環境上の working_directory の値の場所にGitリポジトリをコピーする。
      - checkout

      # dockerコンテナの立ち上げを待つ(これ不要かも…???)
      - run:
          name: wait for database
          command: dockerize -wait tcp://127.0.0.1:3306 -timeout 5m

      # キャッシュがあれば、リストアする。テスト時間の短縮につながる。keysでリストアするキャッシュを複数指定
      - restore_cache:
          keys:
            - v1-dependencies-{{ checksum "Gemfile.lock" }}
            - v1-dependencies-

      # Bundle install dependencies
      - run:
          name: Install dependencies
          command: |
            gem install bundler -v 2.1.4
            bundle install --path=vendor/bundle --jobs 4 --retry 3

      # キャッシュする
      - save_cache:
          key: v1-dependencies-{{ checksum "Gemfile.lock" }}
          paths:
            - vendor/bundle

      # DBのセットアップ
      - run:
          name: Database Setup
          command: |
            bundle exec rake db:create
            bundle exec rake db:schema:load

      # rubocopを実行
      - run:
          name: Rubocop
          command: bundle exec rubocop

      # RSpecを実行
      - run:
          name: Run rspec
          command: |
            bundle exec rspec --profile 10 \
                              --format RspecJunitFormatter \ #gem 'rspec-junit-fomatter'が必要
                              --out test_results/rspec.xml \
                              --format progress \
                              $(circleci tests glob "spec/**/*_spec.rb" | circleci tests split --split-by=timings)

      # Save artifacts
      - store_test_results:
          path: /tmp/test-results
default: &default
  adapter: mysql2
  encoding: utf8mb4
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root
  password: <%= ENV['DATABASE_DEV_PASSWORD'] %>
  host: localhost
  collation: utf8mb4_bin

# 略
test:
  <<: *default
  database: ci_test  # 名前変更(config.ymlに合わせる)
  host: <%=ENV['DB_HOST'] || '127.0.0.1' %> # 追加

CircleCIをローカルで実行する

私は最初の数回、ローカルで実行できるのを知らず、pushして動作確認していました…
ジョブを実行して、エラーを1つづつ潰していけばOK

store_cache, save_cacheは実行されずスキップします。

# CircleCIのインストール
❯ brew install circleci 

# CircleCIの設定ファイルconfig.ymlの文法チェック
❯ circleci config validate .circleci/config.yml

# 文法間違いがあるときの結果
Error: Unable to parse YAML
mapping values are not allowed here
 in 'string', line 12, column 30:
          - image: circleci/mysql: 5.7

# OKなとき
❯ circleci config validate .circleci/config.yml
Config file at .circleci/config.yml is valid.

#ジョブの実行
❯ circleci local execute --job build

遭遇したエラー①

Step failed
====>> Uploading test results
Error: Unable to save test results from /tmp/test-results
Error path is not valid /tmp/test-results: error accessing path: /tmp/test-results: lstat /tmp/test-results: no such file or directory

テストの結果を入れるファイルを用意していなかった。/tmp/test-resultsを作成。

遭遇したエラー②

# --- Caused by: ---
# Mysql2::Error:
#   Unknown database 'ci_test'
#   ./spec/rails_helper.rb:28:in `<top (required)>'

db名を変えたのに、db:createしていなかった。

❯ rails db:create       
Created database 'ci_test'

これでOK

参考にしたもの

CircleCI のローカル CLI の使用 - CircleCI
ローカルで実行
公式サンプルRuby用
公式サンプルRails&Mysql用
bundler設定を参考
.circleci/config.yml内の用語参考
save_cacheとstore_cacheの説明
エラー参考

【環境構築】rails new ~ MySQLの初期設定

rails new

そもそも、railsコマンドを使ったら、command not foundのエラーが出たので、gem install rails install実行してインストール。

rails new オプションいろいろ

rails newコマンドを実行するにあたり、オプションを指定しておきます。

~/workspace/カレントディレクトリ *ブランチ名
❯  rails _6.0.3.4_ new . -B -d mysql --skip-test  
  • アプリケーションに先行してREADMEを作っていたため、カレントディレクトリにアプリケーションを作成したいです。
    .でカレントディレクトリに雛形ファイルを作成できます。
  • -Bオプションで、bundle installをスキップしました。
  • -dオプションで、使用するデータベースをmysqlに指定しています。
  • --skip-testオプションで、テストの生成をパスしています。

railsコマンド(rails) | Railsドキュメント

必要なgemを追記して、bundle installします。

MySQLの設定

こっちが詰まりに詰まり、苦しかったです。一日苦しみました。

MySQLをアンインストール

私はもともとMySQLをインストールしていたようですが、パスワード等をなくしてしまったので一度アンインストールしました。
関連ファイルまで全て削除しないとアンインストールできませんでした。
macOSにHomebrewでインストールしたMySQL5.7, MySQL8.0をアンインストールする方法 | プログラミング入門ナビ

再インストール

ローカルの環境にインストールしていきます。 基本的に、環境構築はプロゲートがわかりやすいです。
MySQLの開発環境を用意しよう(macOS) | プログラミングの入門なら基礎から学べるProgate[プロゲート]

Rails側の設定

ローカルでMySQLのログインまで終わったら、Rails側の設定をします。
config/detabase.ymlでDBの設定をします。

rails newするときにMySQLを指定していたので、自動でファイルが出来上がっています。
passwordは入力する必要があるのですが、Githubにpushしたくないので、環境変数を設定し、そこからパスワードを読み込む設定にします。

環境変数設定

gem 'dotenv'を導入しました。

GitHub - bkeepers/dotenv: A Ruby gem to load environment variables from `.env`.
【Ruby on Rails】GitHubに公開したくない変数や値を隠してpushする方法 - Qiita

DATABASE_DEV_PASSWORD = '設定したパスワード'
default: &default
  adapter: mysql2
  encoding: utf8mb4
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root
  password: <%= ENV['DATABASE_DEV_PASSWORD'] %> # 追加
  host: localhost
略

Githubにpushしないように、gitignoreに追加します。

/.env

DB生成エラー

MySQLの初期設定が終わったので、DBを生成していきます。

エラー発生…

❯ bundle exec rails db:create
rails aborted!
LoadError: dlopen(/Users/ユーザー名/workspace/アプリ名/vendor/bundle/ruby/2.7.0/gems/mysql2-0.5.3/lib/mysql2/mysql2.bundle, 9): Library not loaded: /usr/local/opt/mysql/lib/libmysqlclient.21.dylib
  Referenced from: /Users/ユーザー名/workspace/アプリ名/vendor/bundle/ruby/2.7.0/gems/mysql2-0.5.3/lib/mysql2/mysql2.bundle
  Reason: image not found - /Users/ユーザー名/workspace/アプリ名/vendor/bundle/ruby/2.7.0/gems/mysql2-0.5.3/lib/mysql2/mysql2.bundle

LoadError読み込めてない…?

もしかしたら、gem 'mysql2がインストール失敗している…?

❯ bundle doctor  
The following gems are missing OS dependencies:
 * mysql2: /usr/local/opt/mysql-client/lib/libcrypto.1.1.dylib
 * mysql2: /usr/local/opt/mysql-client/lib/libssl.1.1.dylib
 * mysql2: /usr/local/opt/mysql/lib/libmysqlclient.21.dylib

対処

# アンインストールしてから
❯ bundle exec gem uninstall mysql2

# 再インストール
❯ bundle install

そしたら今度はインストール中にエラー…

open-sslライブラリのパスを通す必要があるよう。

❯ export LIBRARY_PATH=$LIBRARY_PATH:/usr/local/opt/openssl/lib/

このコマンドを実行後、bundle installしたらうまくいきました。

その後、rails db:create rails db:migrate rails serverも成功。

f:id:Study-Diary:20201202162710p:plain
Yay!

エラー対処で参考にしたもの

MySQL起動エラーの対処の仕方【Library not loaded: /usr/local/opt/mysql/lib/libmysqlclient.21.dylib (LoadError)】 - Qiita

【MySQL, Rails】「Gem::Ext::BuildError: ERROR: Failed to build gem native extension.」の対処 - Qiita

Cannot install mysql2 gem for Rails project - Stack Overflow

【環境構築】rbenv globalが効かないとき

Rubyのversion指定で詰まったのでメモ rbenvはインストール済みです。

Rubyインストール

❯ rbenv install 2.7.2

Rubyバージョンを指定

システム全体で使用するRubyのバージョンを指定するために、rbenv globalコマンドを実行。(そもそも、globalは強引でよくないかも) これがうまくいかなかった

❯ rbenv global 2.7.2

rubyのバージョンを確認すると、指定したバージョンに変わっていない…

❯ ruby -v 
ruby 2.6.6

インストール自体はできている

❯ rbenv versions 
  system
  2.6.5
 *2.6.6 (set by /Users/user_name/.rbenv/version)
  2.7.2 

以前はまった、Rubyの参照先も合っている。

❯ which ruby
/Users/user_name/.rbenv/shims/ruby 

/usr/bin/rubyではなく、rbenvでインストールした/.rbenv/shims/rubyを正しく参照している。 study-diary.hatenadiary.jp

結論

rbenv localrbenv globalに優先する。

おそらく、以前にrbenv local 2.6.6を実行しており、その設定が優先されたと思われる。

❯ rbenv local 2.7.2
❯ ruby -v 
ruby 2.7.2p137 (2020-10-01 revision 5445e04352) [x86_64-darwin19]

できた!

ポートフォリオ作成

設計の段階で何を使っていいか戸惑うことがあったのでメモφ(・・ 使ったツールなど、適宜更新していく。

画面遷移図

adobeXDを使用。 画面遷移の順番を指定できる。
リンクも作成できるので、READMEに貼り付けられる。 画面遷移の順番がうまく指定できず悩んだ。
プロトタイプで移動先を指定していても、配置している高さ順が優先されるのを知らなかった。。 www.adobe.com

グラフとかデザインはcanvaが使いやすくてかわいい。 こっちで作ってから、XDへ画像として貼り付けした。 www.canva.com

ER図

ER図はこれで。VScodeでも使えるらしい。 線があらぬ方向に曲がったり慣れるまで時間がかかる。

  • 線がアクティブ状態のときに線のスタイルを選択できる

  • カラムの追加は⌘+D app.diagrams.net

そもそもDB設計について無知だったので、『楽々ERDレッスン』『スッキリわかるSQL入門』を流し読み。

【API】【Rails】エラーメモ

エラーを出しまくったので、繰り返さないようにメモ。

wrong number of arguments

Postmanを使って、articleの更新と削除を試していたのに、できない。 新規作成のPOSTと一覧取得のGETは問題なし。
コードは基礎的なものだしパッと見間違っていなそう…

f:id:Study-Diary:20201128090030p:plain
error

{
    "message": "Internal Server Error",
    "errors": [
        "wrong number of arguments (given 1, expected 0)"
    ]
}

wrong number of arguments引数の数が間違っている・・・?
他のアクションはできているので、createdestroyアクションに関わる箇所が間違ってそう。
ということでよくよく見直してみると、

module Api
  module V1
    class User::ArticlesController < BaseController
      before_action :set_article, only: %i[update destroy]
# 略

      def update
        if @article.update(article_params)
          json_string = ArticleSerializer.new(@article).serialized_json
          render json: json_string
        else
          render_400(nil, @article.errors.full_messages)
        end
      end

# 略
      private
# 略
      def set_article
        @article = current_user.articles.find(params(:id)) # ここ!!
      end
    end
  end
end

params[:id]だった。。。タイポで数時間悩んだ。

namespaceの重複

ユーザー登録用のコントローラーで、createアクションをPostmanで試してみるも、ユーザー登録できない。

module Api
  module V1
    class RegistrationsController < BaseController

      def create
        @user = User.new(user_params)
 …
# 略

ディレクトリ階層はこんな感じ。

f:id:Study-Diary:20201128093958p:plain
ディレクトリ階層

f:id:Study-Diary:20201128092551p:plain
Postman

undefined method 'new' for Api::V1::User::Module

エラー文をみると、Api::V1::User::Moduleでnewメソッドが定義されていない…?

これは自力で解決できずにギブアップしてしまいました。

結論は、「namespaceの重複を防ぐために、::Userとし、絶対参照にする。 」

ただ、理由が分かってなくて、Userという定数を探すためにrailsが自動で上から順番に探索してくるけど、見つからなかったからエラーになった??
以下のリンクを読んでぼんやりとしかまだ分かってないので今度まとめられたら…

 def create
   @user = ::User.new(user_params)

できた。

f:id:Study-Diary:20201128101226p:plain
Postman

Rubyにおけるクラス/モジュール定義関連の仕組みと、Ruby on Railsにおけるautoloadの仕組み - Qiita

ネームスペースが重複する場合はその定数の参照はコンテキストに依存しない絶対的な参照に変更したほうがいい。::User、Admin::Userのように

Railsのネストしたネームスペースのautoload - hokaccha memo

定数の自動読み込みと再読み込み (Classic) - Railsガイド

Rubyでクラス名のはじめに::(コロンを2つ)をつけるとそのクラスの絶対的な位置表現をすることができる - コード日進月歩

【Rails】【API】雑多なメモ

response.headers

HTTPheaderAccessTokenにtokenをセットしたいとき

Provides access to the request's HTTP headers, for example:

response.headers['AccessToken'] = token

Action Controller の概要 - Railsガイド

ActionDispatch::Request

class Net::HTTPResponse (Ruby 2.7.0 リファレンスマニュアル)

POSTMANで確認すると、ちゃんとheadersにtokenが格納されています。

f:id:Study-Diary:20201125152215p:plain
header

HTTPのトークン認証

authenticate_or_request_with_http_tokenを使う
include ActionController::HttpAuthentication::Token::ControllerMethodsモジュールをincludeするのを忘れないこと。

module Api
  module V1
    class BaseController < ApplicationController
      include ActionController::HttpAuthentication::Token::ControllerMethods

      before_action :authenticate

      # サブクラスで使うのでprotected
      protected

      # ApiKey.activeで有効期限内に限定している(activeというスコープを別で定義している)
      # &.userで、レシーバがnilではない場合にApikeyの情報から関連するuserを特定して返す。
      def authenticate
        authenticate_or_request_with_http_token do |token, _options|
          @_current_user ||= ApiKey.active.find_by(access_token: token)&.user
        end
      end

      # @_というのは、インスタンス変数を、ローカルキャッシュとして扱う場合、_をプレフィックスとして付ける慣習がある
      # current_userメソッドを使ってほしいので、直接@current_userを使わないように明示している。
      def current_user
        @_current_user
      end

      private

      def form_authenticity_token; end
    end
  end
end

Rails による API 専用アプリケーション - Railsガイド

ActionController::HttpAuthentication::Token

Rubyの@_って何?? - Qiita

Rails-APIでsorceryを使ったらundefined local variable or method `form_authenticity_token'と怒られた - Qiita