この記事では、RailsでTwitterやInstagramのようなユーザーフォロー機能を実装する際のポイントを紹介します。 1. バージョン情報 macOS:12.6 Ruby:3.1.2 Rails:6.1.6 2. 実装時のポイント 2-1. モデルの関連 Friendshipモデルの作成 $ rails g model Friendship follower_id:integer followed_id:integer --no-fixture --no-test-framework follower_id:フォロー(という行為)をしているユーザーのid 自分にとってのフォロワーじゃなくてフォローをしている人という意味のフォロワー(⇄フォロイー) followed_id:フォローされてるユーザーのid 意味的にはfollowee_idでもいい 生成されたdb/migrate/XXXX_create_friendships.rbに以下を追記。 add_index :friendships, :followed_id add_index :friendships, [:follower_id, :followed_id], unique: true add_index :friendships, [:follower_id, :followed_id], unique: trueが:follower_idで始まっているため、add_index :friendships, :follower_idは定義不要 unique: trueでデータベース側からユニーク制約をつけ、あるユーザーが同じユーザーを2回以上フォローすることを防ぐ(モデル側のバリデーションによる一意性チェックは後述) $ rails db:migrate User/Friendshipの関連付け app/models/friendship.rb class Friendship < ApplicationRecord belongs_to :follower, class_name: "User" belongs_to :followed, class_name: "User" validates :follower_id, uniqueness: { scope: :followed_id } end validates :follower_id, uniqueness: { scope: :followed_id } モデル側のバリデーションによる一意性チェック scopeを指定することでfollower_idとfollowed_idの組み合わせの一意性を保つ(あるユーザーが同じユーザーを2回以上フォローすることを防ぐ) app/models/user.rb class User < ApplicationRecord has_many :active_friendships, class_name: 'Friendship', foreign_key: :follower_id, dependent: :destroy, inverse_of: :follower has_many :passive_friendships, class_name: 'Friendship', foreign_key: :followed_id, dependent: :destroy, inverse_of: :followed end class_name class_nameオプションはhas_manyで指定した関連付け名と実際のモデル名が違う場合に使用するもの。今回のように一つのFriendshipモデルに対して二つの関連(active_friendshipsとpassive_friendships)を持たせたい場合、has_many :friendships(でuser.friendships)だとどちらの関連か区別できない。なのでhas_many :active_friendships(でuser.active_friendships)とする。ただこのままだとRailsは存在しないactive_friendshipsテーブルを探しに行くことになるので、実際に存在するfriendshipsテーブルを使うにはhas_many :active_friendships, class_name: 'Friendship'とする。以上によりactive_friendshipsという関連を実際のモデル名のFriendshipと対応付けることができる。 Railsガイド: Active Record の関連付け - 4.1.2.2 :class_name foreign_key 今回の場合、Railsは自動的にfriendshipsテーブルのuser_idを探しに行くが、friendshipsテーブルにuser_idカラムはないので、foreign_keyオプションで明示的にfollower_id(またはfollowed_id)を指定する必要がある Railsガイド: Active Record の関連付け - 4.1.2.5 :foreign_key dependent: :destroy Userモデルのデータリソースが削除されるとそれに紐づくFriendshipモデルのデータリソースも同時に削除される Railsガイド: Active Record の関連付け - 4.1.2.4 :dependent inverse_of Railsガイド: Active Record の関連付け - 3.5 双方向関連付け # inverse_ofの挙動 irb> a = User.first irb> b = a.active_friendships.first irb> a.name == b.follower.name => true irb> a.name = 'David' irb> a.name == b.follower.name => true # inverse_ofを指定しないとfalse # user(自分)が相手をフォローした場合 user.active_friendships.first.follower #=> 自分 user.active_friendships.first.followed #=> 相手 user.passive_friendships.first.follower #=> 相手 user.passive_friendships.first.followed #=> 自分 2-2. フォロー数・フォロワー数の表示 app/models/user.rb ...
【Rails】ActiveStorageで画像アップロード機能を実装する
この記事では、Rails 5.2からの標準機能であるActiveStorageを使って画像アップロード機能を実装するポイントを紹介します。 1. バージョン情報 macOS:12.6 Ruby:3.1.2 Rails:6.1.6 image_processing:1.12.2 active_storage_validations:0.9.8 2. 実装時のポイント 2-1. Active Storageのセットアップ 以下を実行して、migrationファイルを作成します。 $ rails active_storage:install db/migrate/XXXX_create_active_storage_tables.active_storage.rbが作成される migrationを実行します。 $ rails db:migrate 2-2. 画像アップロードと表示 各userにアバター画像を添付したい場合は、以下のようにUserモデルを定義します。 class User < ApplicationRecord has_one_attached :avatar end 以下のように書くことでフォームからアバター画像をアップロードできます。 <div class="field"> <%= f.label :avatar %> <%= f.file_field :avatar %> </div> ストロングパラメータにavatarの追加が必要 以下のように書くことでアバター画像を表示できます。 <%= image_tag user.avatar if user.avatar.attached? %> avatar.attached?で特定のuserがアバター画像を持っているかどうかを調べられます。 2-3. 画像のリサイズ 画像のリサイズのためにimage_processing gemが必要です。 Gemfileのimage_processing gemのコメントを解除します。 # Use Active Storage variant gem 'image_processing', '~> 1.2' image_processing gemを使うために、macにimagemagickとvipsをインストールします。 $ brew install imagemagick vips GitHub - image_processing - Installation variantメソッドで、添付ファイルごとに特定のサイズ違いの画像を生成できます。 ...
【Rails】deviseでユーザー認証機能を実装する
この記事では、devise gemを使ってRailsアプリケーションにユーザー認証機能を実装する方法を紹介します。 1. 実行環境 macOS:12.5.1 Ruby:3.1.2 Rails:6.1.7 devise:4.8.1 devise-i18n:1.10.2 2. 手順 2-1. サンプルアプリの作成 以下のコマンドを実行して、サンプルアプリを作成します。 $ rails new sample_app $ cd sample_app $ rails generate scaffold book title:string memo:text $ rails db:migrate 2-2. deviseのセットアップ Gemfileに以下を追記して、bundle installを実行します。 gem 'devise' アプリケーションのルートディレクトリで以下のコマンドを実行して、deviseをセットアップします。 $ rails generate devise:install config/environments/development.rbに以下を追記して、デフォルトの url オプションを定義します。 config.action_mailer.default_url_options = { host: 'localhost', port: 3000 } 2-3. ルートルーティングの設定 config/routes.rbに以下を追記して、ルートルーティングを設定します。 Rails.application.routes.draw do root to: 'books#index' end deviseはサインインやパスワード変更などを行った後のデフォルトのリダイレクト先にルート(/)を使用します。 上記の設定により、リダイレクト先が/から/booksに変更されます。 2-4. フラッシュメッセージの表示 app/views/layouts/application.html.erbを以下のように編集します。 <%# 省略 %> <body> <% if notice.present? %> <p id="notice"><%= notice %></p> <% end %> <% if alert.present? %> <p id="alert"><%= alert %></p> <% end %> <%= yield %> </body> app/views/books/index.html.erbとapp/views/books/show.html.erbの以下の行を削除します。 ...
VSCodeでRailsをデバッグする【Rails + vscode-rdbg(debug.gem)】
この記事では、Ruby 3.1で標準ライブラリとなったdebug.gemを使って、VSCode上でRailsアプリのデバッグを行う方法を紹介します。 1. 設定方法 Railsアプリのデバッグを行うために必要な設定を行います。 1-1. debug.gemのインストール ※ Rails 7以降ではデフォルトでdebug.gemが使われるため以下の設定は不要 Gemfileに以下を記述します。 group :development, :test do # See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem gem "debug", platforms: %i[ mri mingw x64_mingw ] end bundle installを実行してdebug.gemをインストールします。 1-2. vscode-rdbgのインストール VSCodeの拡張機能「VSCode rdbg Ruby Debugger」を追加します。 VSCode rdbg Ruby Debugger - Visual Studio Marketplace 1-3. launch.jsonファイルの作成 VSCodeのアクティビティバーから「実行とデバッグ(Run and Debug)」を選択し、「launch.jsonファイルを作成します(create a launch.json file)」を選択します。 次に、「デバッガーの選択(Select debugger)」で「Ruby(rdbg)」を選択します。 するとプロジェクトのルートフォルダに.vscode/launch.jsonが作成されます。 作成された.vscode/launch.jsonを以下のように編集してください。 { "version": "0.2.0", "configurations": [ { "type": "rdbg", "name": "Debug Rails", "request": "launch", "cwd": "${workspaceRoot}", "script": "bin/rails server", "args": [], "askParameters": false, "useBundler": true, }, ] } 以上で設定は終了です。 ...
【Git】グローバルなgitignoreの設定方法
この記事では、グローバルなgitignoreの設定方法を紹介します。 グローバルなgitignoreファイル(~/.config/git/ignore)を作成し、全てのリポジトリでGitの追跡対象外とするファイルを定義します。 1. gitignoreとは 意図的に未追跡のファイルを指定して、それらをGitが追跡しないようにするためのもの すでに Git に追跡されているファイルは影響を受けない 現在追跡しているファイルの追跡を止めるには、git rm --cached を使う 2. gitignoreの使い分け .gitignore:特定のリポジトリで全ての人が無視したいファイル $GIT_DIR/info/exclude:特定のリポジトリで自分だけが無視したいファイル $XDG_CONFIG_HOME/git/ignore:全てのリポジトリで自分だけが無視したいファイル 3. グローバルなgitignoreの設定 グローバルなgitignoreの設定には$XDG_CONFIG_HOME/git/ignoreを使用します。 3-1. $XDG_CONFIG_HOME/git/ignoreとは core.excludesfileのデフォルト値 $XDG_CONFIG_HOMEが未設定の場合、代わりに$HOME/.config/git/ignoreが使用される つまり $ git config --global core.excludesfile ~/.gitignore_globalして~/.gitignore_globalを作成する必要はなく、代わりに~/.config/git/ignoreを作成すればok。 ※~/.config/git/ignoreを使用する場合、core.excludesfileの設定の削除が必要 3-2. 設定方法 ~/.config/git/ignoreを作成し、無視したいファイルを追加するだけです。 gitignoreテンプレート:github/gitignore gitignore作成:gitignore.io 【参考】 Git - gitignore Documentation ファイルを無視する - GitHub ドキュメント gitignore を正しく理解したい - うさぎ小屋 ~/.gitignore_global を指定するのをやめ、デフォルトの置き場に置こう 「$HOME/git/ignore」と 「$GIT_DIR/info/exclude」と「.gitignore」の使い分け - セットプチフォッカ Git - git-rm Documentation XDG Base Directory - ArchWiki 最近目にする$HOME/.configというディレクトリ - 理系学生日記 SourceTree が Git のグローバルな無視リストを書き換えて困った話 - てっく煮ブログ .gitignorがないのにファイルがコミットできないときの対処方法
【Rails】kaminariでページング処理を実装する
この記事では、kaminari gemを使ってページング処理(ページネーション)を実装する方法を紹介します。 1. 実行環境 macOS:12.5.1 Ruby:3.1.2 Rails:6.1.7 kaminari:1.2.2 2. 手順 2-1. kaminari gemのインストール Gemfileに以下を追記します。 gem 'kaminari' 以下のコマンドを実行してkaminari gemをインストールします。 bundle install 2-2. ページネーションを実装 コントローラーに以下を追記します。 def index @books = Book.order(:id).page(params[:page]) end デフォルトで25件/ページ Book.page(params[:page])だと並び順が変わることがあるため、必ずorderをつける Active Record クエリインターフェイス - 4 並び順 order - ActiveRecord::QueryMethods ビューファイルに以下を追記します。 <%= paginate @books %> 以上でページネーションの実装は完了です。 2-3. 日本語化 kaminariが表示する"First"や"Last"のような表示を日本語化します。 config/locales/ja.ymlに以下を追記します。 ja: views: pagination: first: '最初' last: '最後' previous: '前' next: '次' truncate: '...' I18n and Labels - GitHub - amatsuda/kaminari 【参考】 GitHub - amatsuda/kaminari kaminari徹底入門 #Ruby - Qiita 【Rails】 kaminariの使い方を理解してページネーションを実装しよう | Pikawaka
【Rails】i18nで日本語化する方法
この記事では、Railsに同梱されているi18n gemを使ってアプリケーションを多言語化する方法を紹介します。 1. 実行環境 macOS:12.5.1 Ruby:3.1.2 Rails:6.1.7 i18n:1.12.0 2. 手順 2-1. i18nモジュールの設定 使用する言語のリストとデフォルトで使用する言語を設定します。 config/initializers/locale.rbに以下を追記します。 # 英語と日本語の利用を許可する I18n.available_locales = [:en, :ja] # デフォルトの言語を日本語にする I18n.default_locale = :ja 2-2. ロケールファイルのダウンロード 使用する言語のデフォルトのロケールファイルをsvenfuchs/rails-i18nからダウンロードします。 日本語 $ curl -s https://raw.githubusercontent.com/svenfuchs/rails-i18n/master/rails/locale/ja.yml -o config/locales/ja.yml 英語 $ curl -s https://raw.githubusercontent.com/svenfuchs/rails-i18n/master/rails/locale/en.yml -o config/locales/en.yml 上記のコマンドによりconfig/locales/ja.ymlとconfig/locales/en.ymlが作成されます。 以降はこれらのファイルに翻訳を追加していきます。 2-3. Active Recordモデルで翻訳を行なう config/locales/ja.ymlに以下を追記します。 ja: activerecord: models: book: 本 attributes: book: title: タイトル memo: メモ model_name.humanとhuman_attribute_nameを使ってモデル名とカラム名の訳語を表示します。 Book.model_name.human #=> 本 Book.human_attribute_name(:title) #=> タイトル Book.human_attribute_name(:memo) #=> メモ Rails 国際化(i18n)API - 4.7.2 Active Modelのメソッド config/locales/en.ymlに英訳を追加します。 en: activerecord: models: book: Book attributes: book: title: Title memo: Memo 2-4. その他の翻訳 config/locales/ja.ymlに以下を追記します。 ...
【Git】コミット済みファイルを管理対象から除外する方法
Gitは一度ファイルを追跡すると、.gitignoreに追加しても追跡は継続されます。 .gitignoreに追加する前にコミットしてしまった。 リモートリポジトリにすでに追跡されているファイルの追跡をやめたい。 このような既にGitの管理対象になっているファイルの追跡を止めるには以下のコマンドを実行します。 ファイルの場合 git rm --cached <ファイル> ディレクトリの場合 git rm -r --cached <ディレクトリ> 【参考】 Git - git-rm Documentation How do I make Git forget about a file that was tracked, but is now in .gitignore? - Stack Overflow Git: Stop Tracking File After Adding to .gitignore How to Stop Git Tracking a File | Alpha Efficiency.™ gitの管理対象から特定のファイル、ディレクトリを削除する #Git - Qiita
【iTerm2】現在のディレクトリのまま新規タブを開く
iTerm2のデフォルトでは、ホームディレクトリで新規タブが開かれます。 これを、元のタブと同じディレクトリで新規タブを開くように設定します。 1. 実行環境 iTerm2:3.4.16 2. 設定方法 Preference > Profile > Working Directory > Reuse previous session’s directory 以上で設定は終了です。 2-1. 追加設定 Advanced Configuration > Edit… 新規タブのほかに、新規ウィンドウとパネル分割時のディレクトリ設定が可能
【Ruby】Sinatraでメモアプリを作る(DB編)
この記事では、RubyのWebアプリケーションフレームワークであるSinatraを使って、シンプルなメモアプリを作成します。 データ保存先のDBにはPostgreSQLを使用します。 今回は、以下の記事で作成したメモアプリのデータ保存先をPostgreSQLに変更します。 【Ruby】Sinatraでメモアプリを作る(JSONファイル編) | あまブログ PostgreSQLのインストール方法は以下を参照ください。 【macOS】PostgreSQLの基本操作 | あまブログ 1. 実行環境 macOS:12.5 Ruby:3.1.0 Bundler:2.3.12 PostgreSQL:14.3 sinatra:2.2.2 webrick:1.7.0 sinatra-contrib:2.2.2 pg:1.4.3 2. 手順 2-1. pgのインストール RubyからPostgreSQLを使うために必要なgem、pgをインストールします。 Gemfileに以下を追加します。 + gem "pg" gem "sinatra" 以下のコマンドを実行してgemをインストールします。 $ bundle install 2-2. DB接続とテーブル定義 memo.rbでpgを使えるようにします。 - require 'json' + require 'pg' memo.rbの以下の部分を削除します。 FILE_PATH = 'public/memos.json' def get_memos(file_path) File.open(file_path) { |f| JSON.parse(f.read) } end def set_memos(file_path, memos) File.open(file_path, 'w') { |f| JSON.dump(memos, f) } end memo.rbに以下を追加します。 def conn @conn ||= PG.connect(dbname: 'postgres') end configure do result = conn.exec("SELECT * FROM information_schema.tables WHERE table_name = 'memos'") conn.exec('CREATE TABLE memos (id serial, title varchar(255), content text)') if result.values.empty? end PG.connectでDBに接続し、インスタンス変数@connに接続情報をキャッシュ configurationにはアプリ起動時に一度だけ行う処理を記述 PostgreSQLのシステムテーブルtablesからmemosテーブルの存在を確認 if result.values.empty?でmemosテーブルがなければテーブルを作成 2-3. メモ一覧の表示 memo.rbに以下を追加します。 ...