20210106のRailsに関する記事は9件です。

余計なクエリ数を減らしたい。counter_cacheの検討からcounter_cultureの導入まで。

起こっていた問題

Railsでインスタグラムを模したアプリを作っています。
データベース構造はこんな感じです。

Image from Gyazo

このアプリのトップページ(投稿一覧)でこんなメソットを実装していたところ、

  - if post.images.count >= 2
    - # 処理

クエリがこんなことになってしまいました^^;

Image from Gyazo

imagesのcountが大量に回っている...^^;

そういえば、Railsにはcounter_cacheという機能がデフォルトであって、こういうときに役立つんだよ、と聞いたことがあったので、調べてみたのが今回の始まりです。

なお、実行環境は以下の通りです。

  • Rails 5.2.3
  • Ruby 2.6.0

counter_cacheについて

まず、上記の問題を解決する手段として、

の3種類があることがわかりました。(3つ目は教えていただきました!ありがとうございます!!)

まず、Railsのデフォルトの機能のcounter_cacheなのですが、以下のように実装します。

models/image.rb
class Image < ApplicationRecord
  belongs_to :post, counter_cache: true
end

従属している(belongs_to)がついている方のモデルに、counter_cache: trueを記載します。

models/post.rb
class Post < ApplicationRecord
  has_many :images
end

has_manyの方のモデルはそのままなのですが、こちらには、子テーブルの名前_countというカラムを作ります。今回の場合はimages_countというカラムをpostsテーブルに作ります。

$ rails g migration AddColumnToPost images_count:integer

こうすることで、images_countカラムには、postが持っているimageの数が記録される様になります。

ビューでは、こんなふうに書けば、いちいちimagesテーブルを呼び出さずにクエリ数も減るはず...

- if post.images_count >= 2
  -# 処理 

...と、ここまで書いて気がついた。すでにpostのデータがある場合はどうすればいいんだ。。。:sob:

はい、counter_cacheの弱点はまさにそれで、すでにpostsテーブルにデータがある場合、images_countの値は自動更新されません。

先人たちが色々苦労されて修正された様子も見つけたのですが、構造が謎すぎて自分には使いこなせそうにありませんでした。。。

そこで、さらに調べていく中で出会ったのが次に紹介するcounter_cultureです。

counter_cache補足

なお、counter_cacheについては、デフォルトのカラム名を変更したりなど、色々なオプションがありましたので、詳しくはこちらをご覧ください。

Railsガイド - 4.1.2.3 :counter_cache

counter_cultureについて

counter_cultureは上記のcounter_cacheの弱点も補強してくれるような高機能のgemです。

▶︎公式ドキュメントはこちら

導入方法

gemfile・migration

導入方法は以下の通りです。まず、Gemfileに以下を記載しbundle installします。

Gemfile
gem 'counter_culture'

その後、以下のコマンドでマイグレーションを生成します。

$ rails generate counter_culture Post images_count

こちらのコマンドの結果、以下のようなマイグレーションが作成されます。

XXXXXXXXXXXXXXX_add_images_count_to_posts.rb
class AddImagesCountToPosts < ActiveRecord::Migration[5.2]
  def self.up
    add_column :posts, :images_count, :integer, null: false, default: 0
  end

  def self.down
    remove_column :posts, :images_count
  end
end

同様の構造を持つマイグレーションファイルであれば、必ずコマンドで作成しないといけない、というわけではないようです^^

rails db:migrateします。

model

モデルファイルには、それぞれ以下の様に記載します。

models/image.rb
class Image < ApplicationRecord
  belongs_to :post
  counter_culture :post
end
models/post.rb
class Post < ApplicationRecord
  has_many :images
end

その他

そして、すでにpostsテーブルにたくさんデータがあるなどの場合、コンソールで以下の様に実行します。

$ rails c
pry(main)> Image.counter_culture_fix_counts

こうすることで、postsテーブルのimages_countのカラムの中身がアップデートされます。

最後に、ビューでこの様に書けば...

- if post.images_count >= 2
  -# 処理 

無事、クエリ数が減りました!!:smiley:

Image from Gyazo

補足・参考資料等

counter_cultureにはオプションが色々ある様でしたので、詳しく知りたい方は公式ドキュメントをご覧ください。

また、今回参考にさせていただいた記事は以下の通りです。

その他・感想等

今回の実装前に、N+1問題も発生していたので、その解決方法も参考までにご共有いたします。

クエリ数を減らしたいが今年の目標の一つだったのですが、SQLをゴリゴリ勉強する前に、手早く減らせる方法が見つかってよかったです^^

今後は、SQLの勉強を頑張っていきたいです。

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

git push herokuし、herokuのURLを開くとMethod Not Allowedが出る問題

問題

git push herokuすると以下のような表示が出ますよね
ここの下から2行目を開くとMethod Not Allowedがでます。

スクリーンショット 2021-01-06 20.13.12.png

解決法

上の写真の下から2行目はgit用のファイルなので、remote:の後のURLを開きましょう。

リンクを確認せずに開くとこういうことになってしまいます。
気をつけましょう(戒め)

ちなみに

このgitのリンクはデプロイ用のgit repositoryらしいです。
なんらかの問題があった場合の最終手段としてcloneしたりするのはいいらしいですが
正規の”オリジン”リポジトリとしては使用してはいけないそうです。
詳しくは以下のリンクを参照してください。

参照

herokuにgit pushしたURLを開くと、Method Not Allowed と表示されてしまう

Git での既存の Heroku アプリケーションの複製

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

ニコニコ動画apiを使って埋もれてる面白い動画を発掘する

はじめに

  • ニコニコが好きなので作ってみました。

背景

  • ニコニコ動画のとりあえずマイリストの廃止で動画のマイリスト数が激減した
  • マイリストは面白い動画を見分ける判断材料になっていたのでとても残念
  • とりマイの代わりにできた「いいね」の数はなぜか非公開(意味がわからない)
  • 良質な動画を発見する機会と材料がなくなった

概要

  • ニコニコのスナップショットAPIを使って、動画のタグ検索を行い動画リストを作成する
  • 埋もれている動画の基準として、"(コメント数+マイリスト数×100)/再生数"が60%以上で、かつ再生数が500以上の動画
  • csvファイルにて出力

開発環境

  • 言語
    • ruby

検索例(一部)

"実況プレイ動画"タグで検索。
対象の投稿日時は、2020年1月から12月末まで。
全部で1053件の動画が抽出されました。
niconico.png

最後に

スナップショットapiは一度に取得できる動画の数が最大100でyoutubeDataAPIのようにnext_page_tokenがなく、大量のデータを取得するには指定した日付で1週間ごとのループ行ったが少し面倒だった。
とりあえずマイリストを消した運営の判断は本当に意味がわからないし、いいねも機能しているとは思えない。いいね数を開示するか、いいねとマイリストを合算した数を開示しないと、いいねをする理由にはならないと思う。(ユーザーがいいね機能を使う理由が今のところ存在しない。)

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

【circleCI】Rails6/mysql8のProjectを自動テスト&自動コードチェック

はじめに

久しぶりにyml職人していたら、circleCIの書き方を忘れてしまったので、備忘録として残します
折角なので冗長なymlをCircleCI Orbsでスッキリさせてみました
CircleCIで設定する環境と、実際にアプリを運用する環境をできるだけ合わせることがポイント
GithubでPull Requetを作成したら、masterにマージする前に自動でビルドされてテストやリンターが走ったら便利。

前提

  • Githubのアカウントをもっていること、既にRailsのプロジェクトがあること
  • Rspec & Rubocopが設定済みであること
  • Mysqlをつかった例を説明します

開発環境

  • Rails6
  • Mysql8
  • Docker
  • AWS ecs

config.ymlの設定

?全体はこんな感じ
・commitした時とmergeした時にRspec&RUbocopが走る
・mergeしたらecsに自動デプロイ

.circleci/config.yml
version: 2.1

orbs:
  ruby: circleci/ruby@1.1.2
  aws-ecr: circleci/aws-ecr@6.7.0
  aws-ecs: circleci/aws-ecs@1.1.0

jobs:
  test:
    docker:
      - image: circleci/ruby:2.6.3
        environment:
          RAILS_ENV: test
          RAILS_MASTER_KEY: ${RAILS_MASTER_KEY}
          DB_HOST: 127.0.0.1
          DB_USERNAME: 'test_user'
          DB_PASSWORD: 'password'
      - image: circleci/mysql:8.0
        command: --default-authentication-plugin=mysql_native_password
        environment:
          MYSQL_DATABASE: app_test
          MYSQL_USER: test_user
          MYSQL_PASSWORD: password
          MYSQL_HOST: 127.0.0.1
          TZ: "Asia/Tokyo"
    steps:
      - checkout
      - ruby/install-deps
      - run:
          name: Wait for DB
          command: dockerize -wait tcp://127.0.0.1:3306 -timeout 1m
      - run:
          name: Database setup
          command: bundle exec rails db:schema:load --trace
      - ruby/rubocop-check
      - ruby/rspec-test

workflows:
  test:
    jobs:
      - test
  deploy:
    jobs:
      - aws-ecr/build-and-push-image:
          account-url: AWS_ECR_ACCOUNT_URL
          region: AWS_REGION
          aws-access-key-id: AWS_ACCESS_KEY_ID
          aws-secret-access-key: AWS_SECRET_ACCESS_KEY
          create-repo: true
          dockerfile: ./docker/api/Dockerfile
          repo: sample-repo
          tag: "${CIRCLE_SHA1}"
          filters:
            branches:
              only: master
      - aws-ecs/deploy-service-update:
          requires:
            - aws-ecr/build-and-push-image
          family: 'sample-task'
          cluster-name: '${ECS_ARN}'
          service-name: 'sample-api'
          container-image-name-updates: "container=sample-container,tag=${CIRCLE_SHA1}"

circleci/ruby@1.1.2を使って諸々設定

?公式に詳しいことが載ってます
circleci/ruby@1.1.2について: circleCIDeveloper

orbs
orbs:
  ruby: circleci/ruby@1.1.2
今回使っているコマンド
# gemのインストール
- ruby/install-deps # Install gems with Bundler.
# rubocopのcommand
- ruby/rubocop-check # Check the code by Rubocop. You have to add `gem 'rubocop'` to your Gemfile. Enable parallelism on CircleCI for faster checking.
# rspecのcommand
- ruby/rspec-test # Test with RSpec. You have to add `gem 'rspec_junit_formatter'` to your Gemfile. Enable parallelism on CircleCI for faster testing.

circleci/aws-ecr@6.7.0 circleci/aws-ecs@1.1.0を使って諸々設定

?公式に詳しいことが載ってます
circleci/aws-ecrについて: circleCIDeveloper
circleci/aws-ecsについて: circleCIDeveloper

orbs
orbs:
  aws-ecr: circleci/aws-ecr@6.7.0
  aws-ecs: circleci/aws-ecs@1.1.0
今回使っているコマンド&ジョブ
# 必要に応じてAWS CLIをインストールし、設定を行う。Amazon ECRにログインし、イメージをリポジトリにプッシュする。AWS_ACCESS_KEY_IDとAWS_SECRET_ACCESS_KEYの環境変数が必要です
- aws-ecr/build-and-push-image: # Install AWS CLI, if needed, and configure. Log into Amazon ECR and push image to repository. Requires environment variables for AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY. We recommend these be saved in a Project 
# プロジェクトのデプロイ&ローリングアップデート
- aws-ecs/deploy-service-update: # Update an ECS service.

? jobsはtestだけ記述(今回はtest stepのみ記述下が、必要に応じてbuild stepも追加)

  • プロジェクトに合わせてimageを選択
  • それぞれ環境変数を設定
  • database.ymlも環境変数を参照するように設定
jobs
docker:
    - image: circleci/ruby:2.6.3 # projectに合わせる
      environment:
        RAILS_ENV: test
        RAILS_MASTER_KEY: ${RAILS_MASTER_KEY} # circleCIのEnvironment Variablesにmaster.keyを追加しておく
        DB_HOST: 127.0.0.1 # defaultでlocalhostなのでmysqlに合わせる
        DB_USERNAME: 'test_user'
        DB_PASSWORD: 'password'
    - image: circleci/mysql:8.0 # projectに合わせる
      command: --default-authentication-plugin=mysql_native_password # v8から認証の方法が変わった為 commandで認証する
      environment:
        MYSQL_DATABASE: app_test
        MYSQL_USER: test_user # rootでは作成できない為、適当に定義する
        MYSQL_PASSWORD: password
        MYSQL_HOST: 127.0.0.1 # containerのhostを指定
        TZ: "Asia/Tokyo"

?stepcommandを実行
サービス起動まで待機:dockerize を使って他のコンテナ内サービス起動を待つ

steps
steps:
  - checkout
  - ruby/install-deps
  - run:
    name: Wait for DB
    command: dockerize -wait tcp://127.0.0.1:3306 -timeout 1m # dockerize を使って他のコンテナ内サービス起動を待つ
  - run:
    name: Database setup
    command: bundle exec rails db:schema:load --trace # migration
  - ruby/rubocop-check
  - ruby/rspec-test

あとはtestのjobsに先ほど設定したtestを指定して自動でテスト&コードチェックが実行される

workflows
workflows:
  test:
    jobs:
      - test

? masterにmergeされたらDockerイメージをECRにpushする

  • circleCIにAWSの環境変数をそれぞれ設定
deploy:
  jobs:
    - aws-ecr/build-and-push-image:
         account-url: AWS_ECR_ACCOUNT_URL
         region: AWS_REGION
         aws-access-key-id: AWS_ACCESS_KEY_ID
         aws-secret-access-key: AWS_SECRET_ACCESS_KEY
         create-repo: true
         dockerfile: ./docker/api/Dockerfile # pushしたいDockerfileを選択
         repo: sample-repo # ecrのリポジトリ名を選択
         tag: "${CIRCLE_SHA1}"
         filters:
           branches:
             only: master

? masterにmergeされたらデプロイ&ローリングアップデート

  • circleCIにAWSの環境変数をそれぞれ設定
deploy
- aws-ecs/deploy-service-update:
    requires:
      - aws-ecr/build-and-push-image
    family: 'sample-task' # ecsのタスク名
    cluster-name: '${ECS_ARN}' # ecsのクラスター名
    service-name: 'sample-api' # ecsのサービス名
    container-image-name-updates: "container=sample-container,tag=${CIRCLE_SHA1}" # sample-containerの部分を適宜コンテナ名に変更

?コミットしてみて動くか検証


?マージしてしてみて動くか検証

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

【Rails】シングルテーブル継承を使って、テーブルの数を削減する【STI】

シングルテーブル継承(単一テーブル継承、STI)とは

シングルテーブル継承(以下、STI)は、単一の継承階層に所属するクラス群を、ただひとつのテーブルを使って永続化する手法です。

言葉だけでは伝わリにくいので、下図を御覧ください。

スクリーンショット 2021-01-05 18.49.31.png

Postsテーブル(スーパークラス)が、サブクラスに継承する形になります。
しかし、注意点が2点あります。

  • Books,Clothes,Goodsテーブルは実際には存在しない。
  • 個々のデータは全てスーパークラスのテーブルに保存されます。この場合はPostsテーブル。

それでは、STIはどういう場面で使えばいいのでしょうか。

STI継承しないと中間テーブルが再生産が起きる

自分はSTIの存在を知らずにアプリを作っており、下図のようになってしまいました。

スクリーンショット 2021-01-05 18.34.01.png

・・・。

PostsテーブルとしてはBooksとClothesとGoodsの記事を作りたかったのですが。。。

それぞれのテーブルを作ってしまうことで、それぞれの中間テーブル(ここでは、Comments,Likes,Pickupテーブル)が再生産が起きてます。

機能自体はできましたが、ゴチャゴチャしすぎて保守的には最悪ですね・・・。
何より新機能をつける度に3つテーブルを用意する必要があり、骨が折れます?

そこで!STIを使い、Postsクラスをサブクラスに継承させました!
以下の通りです。

スクリーンショット 2021-01-06 13.40.38.png

めっちゃスッキリしましたね!
(画像にはBooks,Clothes,Goodsはテーブルとして書いてありませんが、継承させています。)
これで保守がしやすく、新機能もつけやすくなりました!

やり方(結論)

1. スーパークラスを作る

rails g model postでマイグレーションファイルを作成し、テーブルの設定をします。

migrate/〇〇_create_posts.rb
class CreatePosts < ActiveRecord::Migration[6.0]
  def change
    create_table :posts do |t|
      t.references :user,foreign_key: true

      #省略

      t.string :type #typeカラムを作成します!
      t.timestamps
    end
  end
end

ここで注意したいことは、typeカラムを作成することです。
この中に継承先のクラス名が入り、管理されます。

2. それぞれのクラスを作成し、スーパークラスを継承させる

先程のファイルをマイグレーションしてできたモデルファイルを編集したものです。

models/post.rb
class Post < ApplicationRecord
  belongs_to :user

  #省略

end

それではrails g model book --parent=Postでbookモデルを作成します。
--parent=PARENTオプションを使うことで、マイグレーションファイルを生成せずに済みます。

models/book.rb
class Book < Post
  #こうすることでスーパークラスのアソシエーションやバリデーションが引き継がれます。
end

完成です!

ちなみに!
この状態でBookモデルを保存すると、Postsテーブルのtypeカラムに"Book"が代入された状態で,Postsテーブルに保存されます。

Book.create(title: "本です",.......)

以上でSTIのやり方は終わりです。
最後まで読んで頂きありがとうございました!

参考

Railsガイド
https://railsguides.jp/association_basics.html

[Rails] STI(単一テーブル継承)とメタプログラミングでDRY
https://qiita.com/kidach1/items/789c2e7aebbcfbd2583e

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

[Gem不要!!HTML&CSSのみ]初心者が簡単にドロップダウンメニューを作成する方法

はじめに

ハンバーガーメニューやドロップダウンメニューの作成をしたいけど、Gem導入の方法がよくわからない・JavaScriptは思うようにいかない。
そんな初心者の方におすすめの記事なっております。

開発環境

・Rails6.0.0
・MySQL5.6.50

ドロップダウンメニューの実装

早速ドロップダウンメニューの実装の方法の流れを解説します。
ちなみにドロップダウンメニューとは以下のようなメニューのことです。

f15d1dda5826200510a6a8d6eabad639.png

「...」 を押すとメニューが表示されるものです。

よく見かけますが、JavaScriptでやるには面倒だし、Bootstrap導入のためにGemやjQueryを導入したりするのは初心者には中々ハードルが高めかなと思います。

それでは解説していきます。

1)application.html.erbで初期設定

まず app/views/layouts/application.html.erbBootstrapなど必要なものを読み込みます。

以下は僕がドロップダウンメニューを作成した際に使用したHTMLコードです。

views/layouts/application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title>PlansApp</title>
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>
    <link rel="stylesheet" type="text/css" href="http://yui.yahooapis.com/3.18.1/build/cssreset/cssreset-min.css">
    <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
    <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
    <link href="https://use.fontawesome.com/releases/v5.6.1/css/all.css" rel="stylesheet">

    # ここでbootstrapを読み込んでいます
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
  </head>

  <body>
    <%= yield %>
  # 以下の3行のscriptタグを追記しHTMLで使用できるようにしています
    <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
  </body>
</html>

初期設定はこれで終了です。

2)HTMLを書く

初期設定ができたら、早速ドロップダウンメニューのHTMLを記述していきます。
具体的には公式を見てもらってアレンジしてもらうのがベストですが、参考にした記事のものと、僕が作成したものを紹介しておきます。

参考記事の例
<nav class="navbar navbar-expand-lg navbar-light bg-light">
  <a class="navbar-brand" href="#">Navbar</a>
  <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
    <span class="navbar-toggler-icon"></span>
  </button>

  <div class="collapse navbar-collapse" id="navbarSupportedContent">
    <ul class="navbar-nav mr-auto">
      <li class="nav-item active">
        <a class="nav-link" href="#">Home <span class="sr-only">(current)</span></a>
      </li>
      <li class="nav-item">
        <a class="nav-link" href="#">Link</a>
      </li>
      <li class="nav-item dropdown">
        <a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
          Dropdown
        </a>
        <div class="dropdown-menu" aria-labelledby="navbarDropdown">
          <a class="dropdown-item" href="#">Action</a>
          <a class="dropdown-item" href="#">Another action</a>
          <div class="dropdown-divider"></div>
          <a class="dropdown-item" href="#">Something else here</a>
        </div>
      </li>
      <li class="nav-item">
        <a class="nav-link disabled" href="#">Disabled</a>
      </li>
    </ul>
    <form class="form-inline my-2 my-lg-0">
      <input class="form-control mr-sm-2" type="search" placeholder="Search" aria-label="Search">
      <button class="btn btn-outline-success my-2 my-sm-0" type="submit">Search</button>
    </form>
  </div>
</nav>

僕の場合だとlistタグがあるためレイアウトが崩れるなどの問題がありました。
そこで以下のようにアレンジいました。

アレンジ例
<a class="nav-link" href="#" id="navbarDropdown" role="button" datatoggle="dropdown" aria-haspopup="true" aria-expanded="false">
   <i class="fas fa-ellipsis-h"></i>
</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
   <a class="dropdown-item" href="#">コメント履歴</a>
   <div class="dropdown-divider"></div>
   <a class="dropdown-item" href="#">フォローしているユーザー</a>
   <div class="dropdown-divider"></div>
  <%= link_to 'ログアウト', destroy_user_session_path, method: :delete, class: "dropdown-item" %>
</div>

僕の場合はfontAwesomeでアイコンなどを使用したり、開発中のアプリのためパス指定があるところ・無いところがまばらですが、こんな感じでアレンジしました。

公式ドキュメント

3)CSSの適用させる

ここまでくれば、後はお好みでCSSを記述していくだけです。
参考までに以下に参考記事の記述例を紹介しておきます。

参考記事のHTML7行目の以下のクラスに適用させていきます。

 <div class="collapse navbar-collapse" id="navbarSupportedContent">

なぜ、このクラスに適用させるのかの詳しい解説は参考記事をご覧いただけるとわかりやすいので、ぜひ見てみてください。

【初心者】ハンバーガーメニューの詳細表示後のバーを装飾してみた

参考記事の例
@media screen and (max-width: 992px) {
.collapse.navbar-collapse{
    padding: 10%;
    border-radius: 10px 10px 10px 10px;
    background: linear-gradient(white, #cccccc) !important;
    margin: 15%;
    font-size: 1.7rem;
  }
}

@media screen and (max-width: 992px) {
  // スマホ用
  .collapsing.navbar-collapse{
    padding: 10%;
    border-radius: 10px 10px 10px 10px;
    background: linear-gradient(white, #cccccc) !important;
    margin: 15%;
    font-size: 1.7rem;

  }
}

以上でドロップダウンメニューの実装は終了です。
これを期に、UI/UXのスキルの一部を見に付けてみてはいかがでしょうか。

参考文献

公式ドキュメント

【初心者】ハンバーガーメニューの詳細表示後のバーを装飾してみた

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

rbenvでRubyのバージョンが切り替わらないときの対応

rbenvでRubyのバージョンが切り替わらなかったので、それのメモです。

事象

rbenvでバージョンを切り替えても変わりません

$ rbenv versions
  system
  2.6.6 
* 2.7.1 (set by RBENV_VERSION environment variable)
$ rbenv global 2.6.6
$ rbenv versions
  system
  2.6.6 
* 2.7.1 (set by RBENV_VERSION environment variable)

やったこと

set by RBENV_VERSION environment variableと言われているのですが、特にどこにも書いていません。
また.zshrcexport RBENV_VERSION=2.6.6をすると、rbenvのversionはうまく変更されますが、
rubyのバージョンを確認しようとするとエラーになります。

$ rbenv versions
  system
* 2.6.6 (set by RBENV_VERSION environment variable) 
  2.7.1
$ ruby -v
/Users/naoto.koyama/.rbenv/versions/2.7.1/bin/ruby: invalid option -:  (-h will show valid options) (RuntimeError)

原因

Ruby 2.7の警告を抑制するために.zshrcに書いているRUBYOPTの設定を書いていることが原因でした

$ cat ~/.zshrc
...
export RUBYOPT='-W:no-deprecated -W:no-experimental' # <- これが原因

上記を削除してから以下のコマンドを打てばOKです

$ unset RUBYOPT
$ rbenv global 2.6.6
$ rbenv versions
  system
* 2.6.6 (set by /Users/naoto.koyama/workspace/portal/.ruby-version)
  2.7.1
$ ruby -v
ruby 2.6.6p146 (2020-03-31 revision 67876) [x86_64-darwin20]

参考

rbenvのrubyコマンドがすべて「invalid option -: (-h will show valid options) (RuntimeError)」それRUBYOPTが原因かも

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

Rails6でvue.jsを使った開発を行うための設定に挑戦

Rails x Vue.jsを用いた開発に挑戦している。手探りなので間違っている箇所もあるかもしれないが自分なりに検索してやってみた。そういう記事としてご覧いただけると嬉しいです。

環境構築

①npmを導入(ホームディレクトリでOK)

brew install npm

②npmのパッケージをアプリに導入(アプリのディレクトリで)

npm init

ここで対話プロンプトが起動し、いくつか入力を求められる。()内はnpmコマンドがデフォルトで用意した値。

package name: (a) sample
version: (1.0.0) 0.0.0
description:
entry point: (index.js)
test command:
git repository:
keywords:
author: 
license: (ISC)

name…packageの名前。ソースコードでimportやrequireをする際に使われる。

version…最初なので1.0.0とか0.0.0とかで答えておく。更新するたびにバージョンを変える必要あり

description…パッケージの説明。特に入力しないと以下のように表示される

"description": "This README would normally document whatever steps are necessary to get the application up and running.",

アプリを使うための説明はREADMEに書いたから見てくれや〜って感じのこと言ってる

main…モジュールの中で最初に呼ばれるスクリプトファイル。パッケージをrequireした際に最初に呼ばれるファイル。今回はmain.jsとした。

test command…コマンドを使ってソースコードの実行ができるようにする設定。何も入力せずenterを押すと

"test": "echo \"Error: no test specified\" && exit 1"

こんな感じの値が保存される。

これは

npm test

というコマンドに対してecho以下の出力を出しますよ、という意味。実際に実行すると

% npm test

> アプリ名@0.1.0 test 
> echo "Error: no test specified" && exit 1 ここ

Error: no test specified
npm ERR! Test failed.  See above for more details.

こんな感じでechoのところが出力される。

repository…紐付けるgitリポジトリを指定。.gitが存在している場合特に入力していなくても勝手に現在紐付いているgitリポジトリのurl入力される。

author…パッケージの著作者。

license…パッケージの権利情報。標準ではISCとなっているのでそれに倣った。

対話シェルで入力していないものも含めて最終的に以下のようなpackage.jsonができた。

{
  "name": アプリ名,
  "private": true, (公開したくない場合true)
  "dependencies": {
    "@rails/actioncable": "^6.0.0-alpha",
    "@rails/activestorage": "^6.0.0-alpha",
    "@rails/ujs": "^6.0.0-alpha",
    "@rails/webpacker": "4.3.0",
    "turbolinks": "^5.2.0",
    "vue": "^2.6.12"
  },
  "version": "0.1.0",
  "devDependencies": {
    "vue-loader": "^15.9.6",
    "webpack": "^5.11.1",
    "webpack-dev-server": "^3.11.1"
  },
  "description": "This README would normally document whatever steps are necessary to get the application up and running.",
  "main": "main.js", (パッケージを呼び出した時に最初に呼ばれるソースコード)
  "directories": {
    "lib": "lib",
    "test": "test"
  },
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "repository": {
    "type": "git",
    "url": 紐付けているgitリポジトリ
  },
  "author": 自分で設定した著作者,
  "license": "ISC",
  "bugs": {
    "url": リポジトリのissue
  },
  "homepage": リポジトリ名のreadme
}

③webpack, vue-loaderを導入

#ローカルインストール
npm install webpack vue-loader  --save-dev
npm install vue --save

#グローバルインストール
npm install webpack -g 

これらをインストールするとnode.jsのモジュールがnode_modulesフォルダに大量にインストールされるが、これらはpackage.jsonに依存関係が記述されていることで勝手に読み込んでくれる。よってgitにpushする必要がない。

なのでgitignoreをいじる

node_modules/

こうするとnode_modulesがgitにpushされなくなる。

【参考】

vue.jsを用いた開発の導入

https://qiita.com/m0a/items/34df129d6d8991ebbf86

node_modulesをgitignoreに含める理由とか

https://qiita.com/growsic/items/b2965c0ba3b0aaae1ff8

package.jsonの中身

https://qiita.com/dondoko-susumu/items/cf252bd6494412ed7847

npmコマンドでpermission deniedされたので参照したやつ

https://qiita.com/okohs/items/ced3c3de30af1035242d

vue-loaderってなに

https://vue-loader-v14.vuejs.org/ja/

【次やること】

package.jsonの細かい設定の修正

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

【Rails × Docker】*エラー* Could not find gem 'mysql2 (~> 0.5)' in any of the gem sources listed in your Gemfile.

◆目的
ずっと悩んでいたDockerの課題解決に一歩近ずけたので忘却録のため、記述します!
*補足
私の場合、mysql2がないよ!というエラーが出た際、無理くり手動でmysql2を入れてみました。ですが、Gemfileのmysqlの次に記述されてるpumaがないですよ!というエラーが出たので、別の方法を探していたところwebpackerにたどり着きました。

◆結論
コンテナ上で「rails webpacker:install」このコマンドを打った後、
Gemfile.lockの中身を削除して、
docker-compose up --build コマンドを打ったらコンテナが起動しました^^

root@c9fe1cb41fda:/app2# rails webpacker:install

エラー文

docker run コマンドを入力
mysql2がgemのどのリストを探してもないとエラーがでる。
↓↓↓
Could not find gem 'mysql2 (~> 0.5)' in any of the gem sources listed in your Gemfile.
Run bundle install to install missing gems.

*初回のbuildで docker build . ではなく docker-compose  コマンドでimageを作成するべきだったのかも...

ユーザー名noMBP:app2 ユーザー名$ docker run -it -v /Users/ユーザー名/Desktop/app2:/app2 -p 3000:3000 f5dc18466a56 bash
root@c1f90c58d633:/app2# rails new . --force --database=mysql --skip-bundle
 〜 省略 〜
      create  storage
      create  storage/.keep
      create  tmp/storage
      create  tmp/storage/.keep
      remove  config/initializers/cors.rb
      remove  config/initializers/new_framework_defaults_6_1.rb
       rails  webpacker:install
Could not find gem 'mysql2 (~> 0.5)' in any of the gem sources listed in your Gemfile.
Run `bundle install` to install missing gems.

仮説1 Gemfileにmysqlの記述がないのかもしれない。

catコマンドでGemfileの中身を確認
↓↓↓
Gemfileに gem 'mysql2', '~> 0.5'  が存在している。

git_source(:github) { |repo| "https://github.com/#{repo}.git" }

ruby '2.7.2'

# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '~> 6.1.0'
# Use mysql as the database for Active Record
gem 'mysql2', '~> 0.5'
# Use Puma as the app server
gem 'puma', '~> 5.0'
 〜 省略 〜

仮説2 webpackerに問題があるのではないか。

webpackerインストールの直後にエラーが出ているため、webpackerに問題があるのではないか。
コンテナでrails sしてみるとwebpackerのエラーが出ていてwebpackerを入れてねと言われていることがわかる。
Webpacker configuration file not found /app2/config/webpacker.yml. Please run rails webpacker:install Error: No such file or directory @ rb_sysopen - /app2/config/webpacker.yml (RuntimeError)

root@c9fe1cb41fda:/app2# rails s
=> Booting Puma
=> Rails 6.1.0 application starting in development 
=> Run `bin/rails server --help` for more startup options
Exiting
Traceback (most recent call last):

 〜 省略 〜

     2: from /usr/local/bundle/gems/webpacker-5.2.1/lib/webpacker/configuration.rb:92:in `data'
     1: from /usr/local/bundle/gems/webpacker-5.2.1/lib/webpacker/configuration.rb:95:in `load'
/usr/local/bundle/gems/webpacker-5.2.1/lib/webpacker/configuration.rb:99:in `rescue in load': Webpacker configuration file not found /app2/config/webpacker.yml. Please run rails webpacker:install Error: No such file or directory @ rb_sysopen - /app2/config/webpacker.yml (RuntimeError)

app2の中を確認→webpacker.ymlがない。

調べるとRails6以降はデフォルトでwebpackerが入っているということだったが、
なかったので、試行錯誤して入れてみることにした。

①Dockerfileへ記述してもう一度 build してみる「apt-get install -y webpacker'」
E: Unable to locate package webpacker
ERROR: Service 'web' failed to build : The command '/bin/sh -c apt-get install -y webpacker' returned a non-zero code: 100

ユーザー名noMBP:app2 ユーザー名$ docker-compose up --build
Building web
Step 1/24 : FROM ruby:2.7
 ---> 7e58098089a4
Step 2/24 : ENV BUNDLER_VERSION=2.1.4
 〜 省略 〜
Step 11/24 : RUN apt-get install -y  webpacker
 ---> Running in 283d9d5a7034
Reading package lists...
Building dependency tree...
Reading state information...
E: Unable to locate package webpacker
ERROR: Service 'web' failed to build : The command '/bin/sh -c apt-get install -y  webpacker' returned a non-zero code: 100

②コンテナの中で以下のコマンドを実行

root@c9fe1cb41fda:/app2# rails webpacker:install

Done in 153.13s.
Webpacker successfully installed ? ?

成功!!

Gemfile.lockの中身を削除して docker-compose up --build を実行

無事に起動!!

web_1  | => Booting Puma
web_1  | => Rails 6.1.0 application starting in development 
web_1  | => Run `bin/rails server --help` for more startup options
web_1  | Puma starting in single mode...
web_1  | * Puma version: 5.1.1 (ruby 2.7.2-p137) ("At Your Service")
web_1  | *  Min threads: 5
web_1  | *  Max threads: 5
web_1  | *  Environment: development
web_1  | *          PID: 1
web_1  | * Listening on http://0.0.0.0:3000
web_1  | Use Ctrl-C to stop

まとめ

根本的な解決には至ってないですが、なんとかコンテナを起動させることができました。

1. docker runした際、なぜWebpackerがインストールされずmysqlのエラーが出たのか。
2. 直接コンテナでコマンドを打つことで起動させることができたが、次回起動時問題ないのか

上記2点引き続き調べていきたいと思います。

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