- 投稿日:2020-07-26T23:58:22+09:00
Gitに管理されたファイルを削除したい
.gitignoreに書き忘れてcommitしてしまった時に、
「.gitignoreに追記すれば大丈夫...」
と誤解していませんか?
僕のように..汗後から.gitignoreに追記するだけではすでにGitに管理されたファイルは除去されないので、そんな時の対処方法を書いておきます。
Gitに管理されたファイルを残して除外したい場合
$ git rm --cached [除外したいファイル名]その後は必ず
.gitignore
に除外したいファイルを追記する。
注意箇所:
--cached
を必ずつける!!
つけないとファイルごと削除してしまいます。ファイルごと削除したい場合
$ git rm [削除したいファイル名]ディレクトリごと削除したい場合
$ git rm -r [削除したいディレクトリ]最後に
Gitを使い始めた時に何回か忘れてcommitして、都度調べていたので備忘録として残しておきます。
同じ境遇になった方のお役に立てば嬉しいです。
- 投稿日:2020-07-26T22:09:26+09:00
Active Storageで複数画像の投稿・削除
環境
Ruby 2.5.1
Rails 5.2.4.3やりたい事
Rails標準のファイル管理機能Active Storageを使い、画像を複数を投稿・削除できる機能を実装したい。
まずアプリを作成
$ rails new sample_app $ cd sample_app $ bin/rails db:create別ターミナルで
$ bin/rails shttp://localhost:3000 にアクセスして馴染みの画像が出ていればOK。
modelとcontrollerを作成
今回はpostという名前で。
$ bin/rails g model post name:string $ bin/rails db:migrate $ bin/rails g controller postsルーティング
config/routes.rbRails.application.routes.draw do resources :posts endActive Storageをインストール
$ bin/rails active_storage:install Copied migration 20200726095142_create_active_storage_tables.active_storage.rb from active_storage以下の2つのテーブルを作成するマイグレーションファイルが作成される。
・active_storage_attachments
・active_storage_blobs
migrateしてテーブルを作成。$ bin/rails db:migrate == 20200726095142 CreateActiveStorageTables: migrating ======================== -- create_table(:active_storage_blobs) -> 0.0020s -- create_table(:active_storage_attachments) -> 0.0019s == 20200726095142 CreateActiveStorageTables: migrated (0.0041s) ===============モデルと関連付けて使えるようにする
models/posts.rbclass Post < ApplicationRecord has_many_attached :images endcontrollerを記述
controllers/posts_controller.rbclass PostsController < ApplicationController def index @posts = Post.all end def new @post = Post.new end def create @post = Post.new(post_params) if @post.save flash[:success] = "作成しました" redirect_to posts_path else render :new end end def destroy @post = Post.find(params[:id]) @post.destroy flash[:success] = "作成しました" redirect_to posts_path end private def post_params params.require(:post).permit(:name, images: []) end endviewを作成
投稿一覧ページ
posts/index.html.erb<h1>投稿一覧</h1> <%= link_to '新規投稿', new_post_path %> <%= render @posts %>今回はpostのpartialを作って表示させる。
posts/_post.html.erb<div class="post-partial"> <li id="post-<%= post.id %>"> <%= post.name %> <% post.images.each do |image| %> <%= image_tag(image, width:100) %> <% end %> <%= link_to '削除', post, method: :delete, data: { confirm: '削除してよろしいですか?' } %> </li> </div>新規作成ページ
画像を複数投稿する場合はmultiple: trueが必要
posts/new.html.erb<%= form_with(model: @post, local: true) do |f| %> <div> <%= f.label :name, '名前' %> <%= f.text_field :title %> </div> <div> <%= f.label :images, '画像' %> <%= f.file_field :images, multiple: true %> </div> <div> <%= f.submit '投稿する' %> </div> <% end %>ブラウザから http://localhost:3000/posts にアクセス
新規投稿から画像を複数投稿する事ができた。
任意の画像を削除する
controllerにメソッド追記
編集に必要なedit、updateメソッドを追記します。
controllers/posts_controllers.rbdef edit @post = Post.find(params[:id]) end def update post = Post.find(params[:id]) if params[:post][:image_ids] params[:post][:image_ids].each do |image_id| image = post.images.find(image_id) image.purge end end if post.update_attributes(post_params) flash[:success] = "編集しました" redirect_to posts_url else render :edit end end投稿編集ページの作成
新規投稿ページとほぼ一緒だが、登録されている画像を表示しチェックを入れる部分を追記。
posts/edit.html.erb<%= form_with(model: @post, local: true) do |f| %> <div> <%= f.label :name, '名前' %> <%= f.text_field :name %> </div> <div> <%= f.label :images, '画像' %> <%= f.file_field :images, multiple: true %> </div> <% if @post.images.present? %> <p>現在登録されている画像(削除するものはチェックしてください)</p> <% @post.images.each do |image| %> <%= f.check_box :image_ids, {multiple: true}, image.id, false %> <%= image_tag image, size:"100x100" %> <br> <% end %> <% end %> <div> <%= f.submit '編集する' %> </div> <% end %>indexに編集リンクを追記
posts/index.html.erb<div class="post-partial"> <li id="post-<%= post.id %>"> <%= post.name %> <% post.images.each do |image| %> <%= image_tag(image, width:100) %> <% end %> <%= link_to '編集', edit_post_path(post.id) %> #追加 <%= link_to '削除', post, method: :delete, data: { confirm: '削除してよろしいですか?' } %> </li> </div>確認
ブラウザから編集のリンクをクリックし、削除したい画像にだけチェックを入れる。
任意の画像だけ削除できました。
参考
- 投稿日:2020-07-26T17:17:49+09:00
タスク管理 アプリケーション作成手順
1. アプリケーション作成準備
1-1. 作成するアプリケーションの内容を考える
機能 機能内容 一覧表示 全てのタスクを一覧表示する 詳細表示 一つのタスクの詳細内容を表示する 新規登録 新しいタスクをdbに登録する 編集 登録済みのタスクを編集し、dbを更新する 削除 登録済みのタスクをdbから削除 1-2. アプリケーション名を決め、雛形を作成
rails new アプリケーション名 [オプション] #例) rails new taskmanage -d postgresql1-3. dbを作成する
アプリケーションを作成したディレクトリに移動しdbを作成する
rails db:create1-4. サーバーを起動してみる
以下のコマンドを叩き、
「http://localhost:3000」
にアクセスし、railsのデフォルトページが表示されるか確認rails s2. モデルを作成
モデルの構成要素
モデルに対応するRubyのクラス
キャメルケースモデルに対応するデータベースのテーブル
モデルのクラス名を複数名にしたもの、スネークケース2-1. モデルの属性を設計する
属性 属性名 データ型 nullを許可するか デフォルト値 名称 name string 許可しない なし 詳しい説明 description text 許可する なし 2-2. モデルの雛形を作成
rails g model [モデル名] [属性名:データ型 属性名:データ型 ...] [オプション] #例) rails g model Task name:string description:text2-3. マイグレーションをし、dbにテーブルを追加
作成されたマイグレーションファイルを確認し、rails db:migrateを実行
3. コントローラーとビューの作成
rails g controller コントローラー名 [アクション名 アクション名...] [オプション] #例) rails g controller tasks index show new editRails.application.routes.draw do root to: 'tasks#index' resources :tasks end3. 新規登録機能の実装
3-1. 一覧画面に新規登録ページへ遷移するリンクを追加
= link_to '新規登録', new_task_path3-2. 新規登録画面のためのアクションを実装
tasks_controller.rbdef new @task = Task.new end3-3. 新規登録ページのビューを実装
new.html.slim= form_with model: @task, local: true do |f| .form-group = f.label :name #label = 入力欄の名前の表示とラベル部分クリックで入力欄をフォーカスする = f.text_field :name .form-group = f.label :description = f.text_area :description = f.submit nil3-4. 登録アクションの実装
createアクション = 「登録フォームから送られてきたデータをdbに保存し、一覧画面に遷移」
tasks_controller.rbdef create @task= Task.new(task_params) task.save redirect_to tasks_url, notice:"タスク「#{task.name}」を登録しました。" end private def task_params #Strong parameters = フォームからリクエストパラメータとして送られてきた情報が想定どおり{task: {...}}の形であるかチェックし、必要な情報だけを抜き取るという役割 params.require(:task).permit(:name, :description) endrenderとRedirect_toの違い
render Redirect_to アクションに続けてビューを表示させる アクションを処理した直後にビューを表示せず、別のURLに案内する flashメッセージ
リダイレクト時に、次のリクエストに対してちょっとしたデータを伝える
redirect_to tasks_url, notice:"タスク「#{task.name}」を登録しました。" #タスク登録完了メッセージをしてからリダイレクトするという意味 flash.now[:alert] = "すぐにアラート" #同じリクエスト内の場合は、flash.now4. 一覧表示機能の実装
4-1. 新規登録画面のためのアクションを実装
tasks_controller.rbdef index @tasks = Task.all end4-2. 一覧ページで全てのタスクを表示
@tasks.each do |task| task.name task.created_at5. 詳細表示機能の実装
5-1. 一覧ページから詳細ページへのリンクを追加
index.html.slimlink_to task.name, task_path(task)5-2. 選択されたタスクを詳細表示アクションで取得
tasks_controller.rbdef show @task = Task.find(params[:id]) end5-3. 選択されたタスクを詳細表示アクションで取得
show.html.slim@task.id @task.name @task.created_at @task.updated_at6. 編集機能の実装
6-1. 一覧ページと詳細ページに編集ページへのリンクを追加
index.html.slimlink_to '編集', edit_task_path(task)show.html.slimlink_to '編集', edit_task_path6-2. editアクションとupdateアクションを定義づける
tasks_controller.rbdef edit @task = Task.find(params[:id]) end def update task = Task.find(params[:id]) task.update!(task_params) redirect_to tasks_url, notice: "タスク「#{task.name}」を更新しました。" end6-3. 編集ページのビューを実装
new.html.slim= form_with model: @task, local: true do |f| .form-group = f.label :name #label = 入力欄の名前の表示とラベル部分クリックで入力欄をフォーカスする = f.text_field :name .form-group = f.label :description = f.text_area :description = f.submit nilパーシャルテンプレートを使った共通化
パーシャルテンプレートを使い、新規登録ページと編集ページの共通部分をまとめる
_form.html.slim= form_with model: task, local: true do |f| .form-group = f.label :name = f.text_field :name .form-group = f.label :description = f.text_area :description = f.submit nilパーシャルテンプレートの呼び出し
new.html.slim= render partial: 'form'. locals: {task: @task} #「インスタンス変数@taskを、パーシャル内のローカル変数taskとして渡す」edit.html.slim= render partial: 'form'. locals: {task: @task}7. 削除機能の実装
7-1. 一覧ページと詳細ページに削ボタンを追加
index.html.slim= link_to '削除', task, method: :delete, date: { confirm: "タスク「#{task.name}」を削除します。よろしいですか?"}show.html.slim= link_to '削除', @task, method: :delete, date: { confirm: "タスク「#{task.name}」を削除します。よろしいですか?"}7-2. destroyアクションを定義づける
tasks_controller.rbdef destroy task = Task.find(params[:id]) task.destroy redirect_to tasks_url, notice: "タスク「#{task.name}」を削除しました。" end
- 投稿日:2020-07-26T17:07:37+09:00
【Mac】「Docker」で「Ruby on Rails 6」「React」と「MySQL 8」で環境構築(CRUDのサンプル付)
■ はじめに
SESエンジニアとして、PHPをメインに参画し、現在はJava案件に参画しているTatsuyaです。
そしてこの度転職が決まりまして、9月から新しい職場に!!!!
楽しみもある反面、実は不安要素が。。。それは、、、
次の職場は!!Ruby on Rails!!やったことない!!僕「学習しないと転職した瞬間からお荷物確定ダァー」
と焦り散らかし始めて、先日からRuby on Railsの学習に励み始めたわけなのですが。。。
僕「Rubyの公式チュートリアルめちゃくちゃ大変すぎないか!?さらにHerokuとかも使うの!?結構大掛かり!?」
というので、実は挫折しちゃいました。。。
挫折理由としては、チュートリアル自体が骨太ということもありますが、 Dockerを使った環境構築 も重なって折れちゃいました。(最弱)僕は、直接自分のMacにRuby環境は作りたくなかったんで、なんとしてでもDockerでローカル環境を整えたかったのです。
(次の職場でもDocker使うみたいですしお寿司)ということで、先人の方々が丁寧に書き上げていただいた貴重な文献を元に、実際に動かしながらRubyの動きがわかるような良い感じの環境を作らせていただいて、そこで学習を進めようという方向に舵を切ることになりました。
その過程で、とてもわかりやすく、実際に手を動かしながら理解しやすいと感じた「環境」「サンプルコード」を有り難くミックスさせていただいたので、そちらの結果の方をこの記事でご紹介できればなと思います。
◇ 大変お世話になった貴重な参考文献(無許可です。すいません。)
以下に記載した方々のお力をお借りして、良い感じのサンプルサイトの構築ができました。ありがとうございます。
これから環境構築作業に入っていきますが、丁寧に進めたい!という方はぜひ僕の記事と一緒に偉大なる先人方の記事を見ていただければかなり深まるのではないかと思います。◉ 環境
Docker + Ruby on Rails + MySQLで開発環境を新規構築する
- 丁寧な記事で、詳しくソース毎の解説をしてくれています。またDockerのインストールから記載していただいているので、初めてDockerに触るという方にもオススメです。とてもありがたかったです。
◉ サンプルコード
Rails + React + AjaxでCRUDのサンプルプロジェクト
Rails + React + AjaxでCRUDのサンプルプロジェクト(ソース一式)
- チュートリアル方式で解説を進めてくださっております。大変わかりやすいです。私はまるっとクローンして動かしたのですが、書きながら学びたい方は順に進めていくのも良いと思います。
◉ その他細かい部分でお世話になった記事
Rails on Dockerでcredentialsをeditしたい
◇ 感謝と謝罪
- ※基本的に参考記事様の情報をコピペで使用しております。私が今回取り組んだのは、 環境とサンプルコードのミックス なので、変にアレンジさせずにほぼ原型のままで使用させていただいております。貴重なお時間を割いて書き上げていただいた記事を丸々コピーするような形になりまして、大変申し訳ないという気持ちと、Rubyの学習コストを下げていただいて誠に有り難うございますという感謝と謝罪を先にさせていただきたいと思います。
■ 実際に環境を構築していきましょう
◇ 前提条件
- Mac
- Docker インストール済みであること
- まだの方はこちらの記事から
- git インストール済みであること
◇ 最終的なディレクトリ構成
ディレクトリ構成mpp_react_crud /mysql /Dockerfile /my.cnf /rails /app /bin /config /db /log /node_modules /public /tmp .browserslistrc .gitignore .ruby-version .babel.config.js .config.ru Dockerfile Gemfile Gemfile.lock LICENSE package.json pastcss.config.js Rakefile README.md yarn.lock docker-compose.yml◇ サンプルプロジェクトをクローン
terminal# homeへ $ cd # mpp_react_crud ディレクトリを作成 $ mkdir mpp_react_crud # mpp_react_crud ディレクトリに移動 $ cd mpp_react_crud # サンプルプロジェクトをclone $ git clone https://github.com/TakeshiOkamoto/mpp_react_crud.git # mpp_react_crudプロジェクト の名前を railsに変更 $ mv mpp_react_crud rails◇ docker-compose.yml の作成
terminal# 『◇ サンプルプロジェクトをクローン』の続きから # docker-compose.yml を作成する $ vi docker-compose.yml # vim画面が開いたら、「iキー」でINSERTモードにし、以下のymlをコピペしてください。 # コピペが完了したタイミングで、「:wq」で保存をして終了してください。docker-compose.ymlversion: "3.7" services: db: build: mysql image: mpp_react_crud_db container_name: mpp_react_crud_db ports: - 3306:3306 app: build: rails command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'" image: mpp_react_crud_app container_name: mpp_react_crud_app ports: - 3000:3000 volumes: - ./rails:/rails depends_on: - db◇ MySQL用のDockerfileとmy.cnfを作成
▼ Dockerfile の作成
terminal# 『◇ docker-compose.yml の作成』の続きから # mysql ディレクトリを作成 $ mkdir mysql # mysql ディレクトリに移動 $ cd mysql # Dockerfile を作成する $ vi Dockerfile # vim画面が開いたら、「iキー」でINSERTモードにし、以下のymlをコピペしてください。 # コピペが完了したタイミングで、「:wq」で保存をして終了してください。DockerfileFROM mysql:8.0.18 ENV MYSQL_ROOT_PASSWORD root_pass COPY ./my.cnf /etc/mysql/conf.d/my.cnf RUN mkdir /var/log/mysql RUN chown mysql:mysql /var/log/mysql RUN mkdir /var/run/mysql RUN chown mysql:mysql /var/run/mysql▼ my.cnf の作成
terminal# my.cnf を作成する $ vi my.cnf # vim画面が開いたら、「iキー」でINSERTモードにし、以下のymlをコピペしてください。 # コピペが完了したタイミングで、「:wq」で保存をして終了してください。my.cnf[mysql] default-character-set=utf8mb4 [mysqld] character-set-server=utf8mb4 collation-server=utf8mb4_bin datadir=/var/lib/mysql socket=/var/run/mysql/mysql.sock log-error=/var/log/mysql/mysqld.log pid-file=/var/run/mysql/mysqld.pid port=3306 default_authentication_plugin= mysql_native_password [client] default-character-set=utf8mb4terminal# 最後に mpp_react_crud ディレクトリに戻る $ cd ..◇ Ruby on Rails用のDockerfileとGemfile.lockを作成
▼ Dockerfile の作成
terminal# 『◇ MySQL用のDockerfileとmy.cnfを作成』の続きから # rails ディレクトリに移動 $ cd rails # Dockerfile を作成する $ vi Dockerfile # vim画面が開いたら、「iキー」でINSERTモードにし、以下のymlをコピペしてください。 # コピペが完了したタイミングで、「:wq」で保存をして終了してください。DockerfileFROM ruby:2.6.5 ENV LANG C.UTF-8 ENV APP_HOME /rails RUN apt-get update -qq && apt-get install -y build-essential nodejs # もしyarnでエラーが発生した場合 RUN apt-get update -qq && apt-get install -y curl && curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && apt-get update && apt-get install -y yarn && apt-get install -y vim RUN rm -rf /var/lib/apt/lists/* RUN mkdir $APP_HOME WORKDIR $APP_HOME COPY ./Gemfile $APP_HOME/Gemfile COPY ./Gemfile.lock $APP_HOME/Gemfile.lock RUN bundle install COPY . $APP_HOME EXPOSE 3000▼ Gemfile.lock の作成
terminal# Gemfile.lock を作成 # 空ファイルで良いので、touch コマンドで作成します。 $ touch Gemfile.lock◇ Rails用データベース設定ファイル"database.yml"を編集
terminal# 『◇ Ruby on Rails用のDockerfileとGemfile.lockを作成』の続きから # database.yml を編集 $ vi config/database.yml # vim画面が開いたら、「iキー」でINSERTモードにし、以下のymlをコピペしてください。 # コピペが完了したタイミングで、「:wq」で保存をして終了してください。database.ymldefault: &default adapter: mysql2 encoding: utf8mb4 pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> username: root password: root_pass host: db development: <<: *default database: mpp_react_crud_development test: <<: *default database: mpp_react_crud_test production: <<: *default database: username: password:terminal# 最後に mpp_react_crud ディレクトリに戻る $ cd ..◇ Dockerイメージをビルド
terminal# 『◇ Rails用データベース設定ファイル"database.yml"を編集』の続きから # Docker イメージをビルド $ docker-compose build◇ docker-composeでアプリを起動〜プロジェクト設定
terminal# 『◇ Dockerイメージをビルド』の続きから # サービスの立ち上げ $ docker-compose up -d # -d: バックグラウンドで起動 # bundle インストール $ docker-compose run app bundle install # 各種パッケージのインストール $ docker-compose run app yarn install # マスターキーの生成 # ファイル生成後、credentials.yml.encの編集画面が表示されるので:q!で終了します。 $ docker-compose run -e EDITOR=vim app rails credentials:edit # フォルダの生成 $ mkdir rails/app/assets/images◇ データベース準備
terminal# 『◇ docker-composeでアプリを起動〜プロジェクト設定』の続きから # データベースの作成 $ docker-compose run app rails db:create # 各テーブルの作成 $ docker-compose run app rails db:migrate # 各テーブルの初期データの作成 $ docker-compose run app rails db:seed◇ 完成!
最後にコンテナを再起動しましょう。
terminal$ docker-compose restart
再起動終了後に、http://localhost:3000 にアクセスしてみてください!
すると、以下のようなページが開かれます。以上でサンプルサイトの環境構築終了です!
■ 終わりに
初心者の方だけでなく、経験者の方でもあっても、初めて触る技術の学習にはかなり苦戦すると思います。
そのため、先に「どういうサイトがどのように動いているか」ということを知るだけでも学習効率は上がるんじゃないかなーと思い、今回の作業に取り掛かりました。
まだ私も環境構築をしてすぐにこの記事を書いているので、この環境を使った学習は始めていませんが、実際にログを仕込んでみたりしながら体感的に学ぶ方が飲み込みは早いと考えるので、ガンガンこの環境とこのサイトを元に学習に取り組んでいこうと思います。この記事は解説部分を完全に端折って、ただただ完成させることだけを目標に書き上げました!
そのため、しっかりと解説を確認したい方は、参考文献様の記事をご確認いただいて、照らし合わせながら進めてほしいと思います。僕「本当に先人の方々はすげえや。」
- 投稿日:2020-07-26T15:31:23+09:00
Rails 環境構築 railsコマンドでのエラー
railsで環境構築をしている際に
rails server
コマンドを打ってもrailsコマンドが使えない、バージョン管理でのエラーがいくつか発生した。各エラー内容と解決した方法をまとめておく。実行環境
OS: MacOS
rubyバージョン管理:rbenv
railsバージョン:6.0.3.2
シェル:zshエラー内容
$ rails serverとうつと
Rails is not currently installed on this system. To get the latest version, simply type: $ sudo gem install rails You can then rerun your "rails" command.Railsは現在このシステムにはインストールされていません。
sudo gem install rails
というコマンドで最新版のRailsをインストールすることができます。とのエラーが発生した。やったこと
ここはエラーで指摘された通り
$ sudo gem install railsを実行した。とりあえずこれでrailsコマンドを使うことはできた。
$ rails -v Rails 6.0.3.2と打ってもバージョンがちゃんと表示され、railsコマンドが使えるようになった。
他の記事などをみているとこれを行なってもエラーが解消されないことが発生している。
調べた範囲ではrbenvでrubyのバージョン管理を行っているとgemの参照先によるエラーもあるそうで、これはPATHを指定してやることで解決するそう。
rbenv自体がデフォでrubyとgemを持っているので、gemを使ってもデフォルトのrubyが起動してしまうためだそうだ。エラー内容2
$ rails serverを打つと
warn_for_outdated_bundler_version': You must use Bundler 2 or greater with this lockfile. (Bundler::LockfileError)という違うエラーが発生した。このロックファイルでは、Bundler 2 以上を使用する必要があります。とのことでbundlerのバージョンが適正でない?という内容。
やったこと2
$ gem update --systemRubyGemsのバージョンアップを実行。そして
$ bundle update --bundlerbundlerのアップグレートを実行。
最後に
rails server
を実行したらPlease run rails webpacker:installと出たので
$ bundle exec rails webpacker:installを実行することでサーバーの起動が確認できた。
- 投稿日:2020-07-26T14:56:56+09:00
ランキング機能のつくりかた【Railsの内部結合と外部結合を理解する】
概要
この記事は、実際に私がBooQsで運用している「ランキング機能」を例にして、Railsのテーブル結合(内部結合と外部結合)を解説する記事です。
前提条件
Rails 5.1
【?♂️Before】テーブル結合を使わないランキング機能
私が開発しているBooQsという英単語学習サービスでは、ゲーミフィケーションを用いてユーザーの学習意欲を高めるために、英単語の解答数(userのもつanswer_historiesの数)によるユーザーのランキング機能を実装しています。
しかし、以下のNewrelicのダッシュボードを見ていただくとわかるように、自分の書いたランキング機能の処理速度はお世辞にも良いものとは言えませんでした。
それではまず、自分が組んだ(下手くそな)コードから見ていきましょう。
恥ずかしながら、自分はテーブル結合をよく理解していなかったため、次のようなまどろっこしいコードでランキング機能を実装していました。home_controller.rbdef user_ranking ## ここから!! rankers = [] user_ids = AnswerHistory.where(created_at: Time.now.all_month).group(:user_id).order('count(user_id) desc').pluck(:user_id) user_ids.each do |id| #user_idがnilである非ログインユーザーの解答履歴を除外する。 if id.present? user = User.find(id) rankers << user end end @users = rankers.paginate(page: params[:page], per_page: 10) ## ここまでがわいの書いたうんこーど!!!??? #ページネーションの位置に応じて、最初に表示する順位を調整するための処理 if params[:page].present? @base_of_ranking = params[:page].to_i*10+1-10 else @base_of_ranking = 1 end end以下に、modelとviewも記載します。
model
models/user.rbclass User < ApplicationRecord has_many :answer_histories, dependent: :destroy endmodels/answer_history.rbclass AnswerHistory < ApplicationRecord # 非ログインユーザーの解答記録は、user_idをnilで記録する。 belongs_to :user, optional: true endView
views/home/user_ranking.html.erb<% provide(:title, "月間ランキング") %> <%= render "home/navbar" %> <div class="box users_index"> <div class="wrapper"> <div class="headline-green"> <h1 class="center green"><i class="fas fa-user-crown"></i>解答数ランキング</h1> </div> <p> </p> <div class="quiz_tab"> <%= link_to home_user_ranking_path, class: "left btn green" do %> 月間 <% end %> <%= link_to home_user_ranking_weekly_path, class: "right btn" do %> 週間 <% end %> </div> <ul class="users"> <% @users&.each_with_index do |user, i| %> <% i += @base_of_ranking %> <div class="whole_link"> <li class="user-feed"> <%= link_to user_path(user) do %> <%= icon_for user, size: 50 %> <% end %> <div class="right-side"> <div class="name"> <%= link_to user do %> <% if user.premium_member? %> <i class="fas fa-crown non-margin"></i> <% end %> <%= user.name %> <% end %> </div> <p>ランク:<b><%= i %>位</b></p> <p>月間解答数:<b><%= user.answer_histories.where(created_at: Time.now.all_month).count %></b></p> <p>継続日数:<b><%= running_days_count(user) %></b></p> <p>レベル:<b>Lv.<%= user.current_level.floor %></b></p> <div class="follow_btn"><%= render 'users/follow_form', user: user %></div> </div> </li> <a href="<%= user_path(user) %>" class="link"></a> </div> <% end %> </ul> <div class="center"> <%= will_paginate @users, previous_label: '← 前へ', next_label: '次へ →', page_links: false %> </div> </div> </div>実際のランキングページは以下になります。
ランキングページ: https://www.booqs.net/home/user_ranking
【?♂️After】内部結合を使ったランキング機能
Beforeのテーブル結合を利用しない方法だと、user_idsを取得するためにクエリを発行し、さらにユーザーを取得するたびに、
user = User.find(id)
でクエリが発行されるため、処理が遅くなります。そのため、知り合いのベテランの開発者の方に、下のような方法をオススメいただきました。
home_controller.rbdef user_ranking ## ここから!!! @users = User.joins(:answer_histories).where(answer_histories: {created_at: Time.now.all_month}) .group(:id).order('count(answer_histories.user_id) desc') .paginate(page: params[:page], per_page: 10) ## ここまでが修正いただいたコード!!!??? #ページネーションの位置に応じて、最初の順位を調整するための処理 if params[:page].present? @base_of_ranking = params[:page].to_i*10+1-10 else @base_of_ranking = 1 end endめちゃくちゃスッキリ!!
さらにクエリも一発でとても効率的...!!ただこのコードをコピペするだけでは自分の勉強にはならないので、
ここからは、これらの処理がどんなことをしているのかを1つずつ丁寧に解説していきます。
内部結合【
.joins(:answer_histories)
】
.joins(:answer_histories)
を利用することで、usersテーブルとanswer_historiesテーブルを結合して、データベースを検索できるようになります。つまり、2つのテーブル同士を合体させて、1つのテーブルをつくれるのですね!
BooQsの例で、解説しましょう。
まず、BooQsには以下のように、
会員登録したユーザーのデータを格納するusersテーブル
と、そのユーザーが解いた問題のデータを格納するanswer_historiesテーブル
があります。usersテーブル
id name 1 清水さん 2 小林さん 3 長谷川さん 4 山田さん anwser_historiesテーブル
id quiz_id user_id 1 104 4 2 89 nil 3 95 2 4 184 4 5 43 1 6 205 1 7 21 nil 8 76 1 9 164 1 (補足:user_idがnilであるanswer_historiesは、ログインしていないユーザーの解答履歴です。)
そして、
User.joins(:answer_histories)
を利用することによって、answer_historiesテーブルの外部キー(user_id)に基づいて、2つのテーブルを結合することができます。では、実際に見てみましょう。
usersテーブルにanswer_historiesテーブルを内部結合したテーブル
id name id quiz_id user_id 4 山田さん 1 104 4 2 小林さん 3 95 2 4 山田さん 4 184 4 1 清水さん 5 43 1 1 清水さん 6 205 1 1 清水さん 8 76 1 1 清水さん 9 164 1 右端のanswer_historisの外部キー(user_id)に基づいて、
きちんと2つのテーブルがつながりましたね!結合結果のテーブルの中ですぐに目につく変化としては、次の3つがありますね!
- answer_historiesのuser_id(外部キー)の数だけ山田さんと清水さんが増殖した。
- answer_historiesのuser_idに存在しない長谷川さんは排除された。
- user_idがnilであるanswer_historiesのidが2と7のレコードが排除された。
(注意:例ではanswer_historiesの主キー(id)の昇順でレコードを並べていますが、私の調べた限りでは、結合結果のレコードの順序については、特定のidを基準にした昇順・降順などはないようでした。もし私が間違っていたら誰かぜひ教えてください。)
このようなjoinsメソッドを利用したテーブル同士の結合方法は、【内部結合】と呼ばれます。
内部結合では、結合条件に合致しないレコードは排除されます。
内部結合の結合条件とは、
結合先の外部キー = 結合元の主キー
です。BooQsの例で紹介しましょう。
たとえば、今回のUser.joins(:answer_histories)
によって内部結合を行うと、次のようなSQLが発行されます。「内部結合」で発行されるSQLSELECT "users".* FROM "users" INNER JOIN "answer_histories" ON "answer_histories"."user_id" = "users"."id"上記SQLの
ON "answer_histories"."user_id" = "users"."id"
の部分が、テーブルの結合条件となります。つまり、answer_historiesレコードの外部キー(user_id)とusersレコードの主キー(id)が一致するかどうかが、レコード同士を結合する条件となるのですね。
そして内部結合においては、この結合条件に合致しないusersテーブルとanswer_historiesテーブルのレコードは、結合結果のテーブルから排除されています。具体的に見ていきましょう。
上の結合されたテーブルをじっくりと眺めてください。
きっと次の2種類のレコードが排除されていることがわかるはずです。
- answer_historiesの外部キー(user_id)に存在しないusersレコード(例:長谷川さんのレコードが排除されている)
- 外部キーがnilであるanswer_historiesレコード(例:idが2と7のレコードが排除されている)
具体例があると、きっとわかりやすいと思います。
別の表現で説明すると、内部結合では、以下に示すようなテーブル結合結果にはならない ということです。
(以下で紹介する例は、テーブル結合のうち「外部結合」の解説でもあります。
ランキング機能では「内部結合」を利用するので、読み飛ばしていただいても構いませんが、読んでおくと、ランキングで利用する「内部結合」についても理解が深まるはずです。)
【こうはならない!!】answer_historiesの外部キーに存在しないusersレコードまで結合される【左外部結合】
id name id quiz_id user_id 4 山田さん 1 104 4 2 小林さん 3 95 2 4 山田さん 4 184 4 1 清水さん 5 43 1 1 清水さん 6 205 1 1 清水さん 8 76 1 1 清水さん 9 164 1 3 長谷川さん nil nil nil 上記のテーブルでは、answer_historiesレコードのuser_idに存在しないはずの「長谷川さん」までテーブルに結合されています。
長谷川さんは人生でただ一回もBooQsで問題を解いてくれなかったので、長谷川さんに結合されたanswer_historiesレコードのデータは、なんと、すべてnilです!!(解いてよ!!!?)これは内部結合ではありません。
これは【外部結合】と呼ばれています。
さらに言えば、外部結合のうち、【左外部結合】と呼ばれるテーブル結合方法です。
内部結合と外部結合の区別は簡単です。
外部結合では、内部結合では排除されていた「結合条件に合致しないレコード」まで結合することができます。外部結合には、【左外部結合】と 【右外部結合】の2種類がありますが、こちらもいかめしい文字面よりは、区別も簡単です。
左外部結合は、結合条件に合致しない結合『元』のレコードを結合します。
右外部結合は、結合条件に合致しない結合『先』のレコードを結合します。今回の例でいえば、長谷川さんという、結合条件に合致しない「usersテーブル(結合元)のレコード」を結合しています。
だから、【左外部結合】なのですね。Railsによる左外部結合は、次のように行えます。
Railsにおける「左外部結合」のやり方.rbUser.joins("LEFT OUTER JOIN answer_histories ON users.id = answer_histories.user_id") #Rails5.0以降は、次のメソッドで右外部結合を行うこともできます。 User.left_outer_joins(:answer_histories)【こうはならない!!】外部キーがnilであるanswer_historiesレコードまで結合される【右外部結合】
id name id quiz_id user_id 4 山田さん 1 104 4 nil nil 2 89 nil 2 小林さん 3 95 2 4 山田さん 4 184 4 1 清水さん 5 43 1 1 清水さん 6 205 1 nil nil 7 21 nil 1 清水さん 8 76 1 1 清水さん 9 164 1 上記のテーブルでは、user_idがnilであるanswer_historiesレコードも結合されてしまっています。
user_idがnilということは、これは「会員登録していないユーザーの解答記録」ということなので、当然、結合されたusersレコードのデータもすべてnilです(会員登録してよ!!!?)。このテーブルを生成する結合方法は、「外部結合」のうち、「右外部結合」と呼ばれています。
先ほど説明したように、結合条件に合致しない、結合『先』のレコードを結合するので、右外部結合なのですね。
answer_historiesテーブルは、結合先のテーブルです。右外部結合は、Railsでは次のコードで行えます。
Railsにおける「右外部結合」のやり方.rbUser.joins("RIGHT OUTER JOIN answer_histories ON users.id = answer_histories.user_id")(Railsの右外部結合については、なぜか1つも解説記事が見当たりませんでした。
この記事がRailsによる右外部結合について触れた最初の記事かもしれません。
外部結合(左外部結合&右外部結合)をきちんと理解されたいなら、以下の記事を参考にされると良いでしょう。
SQL素人でも分かるテーブル結合(inner joinとouter join))
ランキング機能をつくる場合、『内部結合によるレコードの排除』はとても都合の良い処理です。
なぜなら、排除される
answer_historiesの外部キーに存在しないusersレコード
とは、すなわち、「1問も問題を解いていないランキング圏外のユーザーのデータ」であり、
外部キーがnilであるanswer_historiesレコード
とは、すなわち、「ログインしていないユーザーの解答記録」であるため、ランキングをつくる上で考慮する必要のないデータだからです。
結合先のレコードの絞り込み【
.where(answer_histories: {created_at: Time.now.all_month})
】Where句を使って、結合先のカラムの値を条件にして、レコードを絞り込むことも可能です。
その場合、
where(結合先のテーブル: {結合先のテーブルのカラム: 値})
という形で行います。BooQsの例で言えば、『月間の』解答数ランキングを表示したかったので、次のように結合先のデータを絞り込んでいます。
「月間」の解答数で絞り込む.rb@users = User.joins(:answer_histories).where(answer_histories: {created_at: Time.now.all_month})ランキングの順位順にusersレコードを並べ替える
【.group(:id).order("count(answer_histories.user_id) DESC")】
さて、ランキング圏内のユーザーのレコードはすべて揃えたので、最後にこれを順位順に並べ替えましょう。
ここまで内部結合を使って作成したテーブルは次のようになっていますよね。
id name id quiz_id user_id 4 山田さん 1 104 4 2 小林さん 3 95 2 4 山田さん 4 184 4 1 清水さん 5 43 1 1 清水さん 6 205 1 1 清水さん 8 76 1 1 清水さん 9 164 1 明かに、清水さんが1位、山田さんが2位、小林さんが3位という風にわかりますが、
これをプログラムで集計するためには、groupメソッド
でユーザーごとにレコードをまとめる必要があります。BooQsの例でいえば、
.group(:id)
でそれぞれのユーザーのレコードをまとめらていますね。さて、groupメソッドでまとめたら、それぞれのグループがもつレコードの数で順位をつければ、ランキングを完成させることができます。
ここで使われるのが、
orderメソッド
です。
orderメソッド自体は、馴染みのある方ばかりでしょうが、実はこのorderメソッド、中身でSQLも利用できます。そのため、groupメソッドによる
GROUP BY
、orderメソッドによるORDER BY
の2つを組み合わせて次のようにすれば、「レコードの多い順(降順)にユーザーを並べ替え」ができ、結果としてランキングの順位でユーザーを並べることができます。ランキング順位でユーザーを並べ替える.rb@users = User.joins(:answer_histories).where(answer_histories: {created_at: Time.now.all_month}) .group("id").order("count(answer_histories.user_id) DESC")orderのなかで、
.order("count(answer_histories.user_id) DESC")
という風に条件を指定していることに注目してください。
この"count([数えたい要素]) 並び順"
という指定方法は、RubyでもRailsでもなく、SQLによる指定方法です。発行されるSQLのクエリは、次のようになっています。
ランキング順位で並べ替え時に発行されるSQLSELECT "users".* FROM "users" INNER JOIN "answer_histories" ON "answer_histories"."user_id" = "users"."id" GROUP BY "users"."id" ORDER BY count(answer_histories.user_id) DESCこのようにgroupメソッドとorderメソッドをうまく使うことで、ランキングの順位でユーザーを並べ替えることができます。
id name 1 清水さん 4 山田さん 2 小林さん あとはこれを上記のviewで
each_with_index
などで取り出してあげれば、ランキング機能の完成です。お疲れさまでした!!
ランキングページ: https://www.booqs.net/home/user_ranking
これだけ教えてください?♂️
これだけ本当にわからなかったので、わかる方がいらしたらぜひ教えてください。。。
内部結合したあとの、users.idとanswer_histories.user_idは同じはずだと思います。
なので私は、下のコードは、
@users = User.joins(:answer_histories)
と全く同じだと思っていました。
.group("id").order("count(answer_histories.user_id) DESC")ランキング順位でユーザーを並べ替える.rb@users = User.joins(:answer_histories) .group("answer_histories.user_id").order("count(answer_histories.user_id) DESC")しかし、このコードを実行すると
ActiveRecord::StatementInvalid (PG::GroupingError: ERROR: column "users.id" must appear in the GROUP BY clause or be used in an aggregate function
というエラーが表示されてしまいます。
つまり、group(GROUP BY)のなかに「users.id」を利用しろと言われてしまいます。
なぜ、groupのなかは、"answer_histories.user_id"
ではダメで、"id"
でなくてはならないのでしょうか??何卒ご教示いただければ幸いです...!?♂️
- 投稿日:2020-07-26T14:00:26+09:00
Chromium 版 Edge を使った system test(2020/7/26 時点)
はじめに
macOS で Chromium 版 Edge を利用するようになったけど、そういえば Rails の system test で使えるのかな? と思ったのでやってみました。2020/7/26 時点の話です。
Edge 用 WebDriver の準備
Chromium 版・旧版の両方の WebDriver を WebDriver - Microsoft Edge Developer からダウンロードします。zip ファイルを解凍し、自身の PATH 環境変数が通ったところに配置しましょう。
$ cd ~/Downloads $ ls msedgedriver* $ cd ~/bin/ $ mv ~/Downloads/msedgedriver .selenium-webdriver gem alpha版
調べてみたところ selenium-webdriver gem の CHANGES に Chromium 版 Edge に対応した旨の記載がありました。
4.0.0.alpha3 (2019-07-08) ========================= (省略) Edge: * Add support for Chromium based implementationということなので
Gemfile
に以下のように記載します。現時点では alpha6 が最新版です。Gemfile# テスト環境 限定 group :test do (省略) # gem 'selenium-webdriver' # NOTE: Edge Chromium 版利用時は 4.0.0 系が必要 gem 'selenium-webdriver', '~> 4.0.0.alpha6' endまた
test/application_system_test_case.rb
にも以下のように記載します。test/application_system_test_case.rb# driven_by :selenium, using: :headless_chrome, screen_size: [1150, 950] # driven_by :selenium, using: :chrome, screen_size: [1150, 950] # driven_by :selenium, using: :firefox, screen_size: [1150, 950] # driven_by :selenium, using: :headless_firefox, screen_size: [1150, 950] # driven_by :selenium, using: :safari, screen_size: [1150, 950] # Chromium 版 Edge 利用時は selenium-webdriver 4.0.0.alpha3 以上が必要 driven_by :selenium, using: :edge_chrome, screen_size: [1150, 950]これで
bin/bundle update && bin/rails test:system
すれば Chromium 版 Edge が起動して system test が走ります。めでたしめでたし :)宿題
headless モードでは未だ動かせてません。
selenium-webdriver
の対応待ちなのかな…
- 投稿日:2020-07-26T13:27:12+09:00
gem 'kaminari' でページネーション実装
- 投稿日:2020-07-26T13:04:45+09:00
Ruby入門2
initialize
newメソッドでオブジェクトを生成するとき、そのオブジェクトのinitializeメソッドが実行される
class User attr_reader :name, :address, :email def initialize(name, address, email) @name = name @address = address @email = email end endprivate
privateと書かれた行より後に定義されたメソッドはprivateメソッドとなり、オブジェクトの内部からは利用できるが、外部からは利用不可
class Person def initialize(money) @money = money end def billionaire? money >= 10000000 end private def money @money end end継承
既存のクラスが持っている機能を基本的に全部引き継いだ上で、一部を変えたいときに使用
親クラスが持つメソッドの処理を、子クラスの書かれた処理で上書きすることを「オーバーライド」という。
※親クラスを呼びたいときはsuperを使うclass PricedObject #親クラス、スーパークラス def total_price price * Tax.rate end def price raise NotImplementedError end end class product < PricedObject #子クラス、サブクラス attr_accessor :price end class OrderedItem < PricedObject #子クラス、サブクラス attr_accessor :unit_price, :volume def price unit_price * volume end endモジュール
Rubyの基本単位はオブジェクトであり、オブジェクトの設計図としてクラスがある。
この他、Rubyにはある一連の振る舞いの設計図を一箇所にまとめた存在として「モジュール」という概念があるモジュールとクラスの違いは、モジュールはオブジェクトを生成することができない。
モジュールは一連の振る舞いを表しており、それを「include」を使い、まとめてクラスに取り込んでもらうことができる。
module Talking def talk "ワンワン" end module Walking def walk "テケテケ" end end class Dog include Talking include Walking end mugi = Dog.new mugi.talk mugi.walk#継承を使った書き方 class PricedObject def total_price price * Tax.rate end def price raise NotImplementedError end end class product < PricedObject attr_accessor :price end class OrderedItem < PricedObject attr_accessor :unit_price, :volume def price unit_price * volume end end #モジュールを使った書き方 module PricedHolder def total_price price * Tax.rate end end class product include PriceHolder attr_accessor :price end class OrderedItem include PriceHolder attr_accessor :unit_price, :volume def price unit_price * volume end end例外捕捉
- 例外が発生するかもしれないコードをbeginのなかに記述
- その中で発生した例外への対応の仕方をrescueの中に記述
- さらに、例外が出た場合も出なかった場合も必ず行いたい後処理をensureののなかに記述
begin #例外が発生するかもしれないコード rescue #例外に対応するコード ensure #例外が発生してもしなくても必ず実行したいコード endnilガード
もし変数がnilだった場合に値をいれる構文
#例1 a = nil a ||= 3 puts a # ==> 3 #例2 def people @people ||= [] end # ==>@peopleがnilである場合も、このメソッドが呼び出された時は空の配列が代入されて返される。ぼっち演算子(safe navigation operator)
&.を使ってメソッドを呼び出すと、レシーバがnilであった場合でもエラーが発生しない。
#ifを使った場合 name = if object object.name else nil end #ぼっち演算子を使った場合 name = object&.name%記法
配列の全ての要素が文字列の場合、「%w」を使って配列を記述可能
array1 = ['apple', 'banana', 'orange'] #==>["apple", "banana", "orange"] #↓同じ array1 = %w(apple banana orange) #==>["apple", "banana", "orange"]全ての要素がシンボルでる配列は、「%i」を使って配列を記述可能
array1 = [:apple, :banana, :orange] #==>[:apple, :banana, :orange] #↓同じ array1 = %i(apple banana orange) #==>[:apple, :banana, :orange]
- 投稿日:2020-07-26T08:36:25+09:00
【Rails+EC2+puma】rails s -e productionは問題ないのに、rails s -dで起動するとエラーになる
環境
Rails: 6.0.2.2
ruby: 2.7.1
Puma: 4.3.5EC2+Nginx+puma
pumaのソケット通信を利用発生した問題
作成したアプリケーションをEC2へデプロイしました。
本番環境での起動も問題なかったのでデーモンで起動しようとしたのですが、pumaがうまく起動していないようで、ブラウザからアクセスすると「We're sorry, but something went wrong.」の文字が...$ rails s -d -e production => Booting Puma => Rails 6.0.2.2 application starting in production => Run `rails server --help` for more startup optionsうまく行っていれば下記のように表示されるはずです。
=> Booting Puma => Rails 6.0.2.2 application starting in production => Run `rails server --help` for more startup options Puma starting in single mode... * Version 4.3.3 (ruby 2.7.1-p83), codename: Mysterious Traveller * Min threads: 0, max threads: 16 * Environment: production * Daemonizing...原因
「We're sorry, but something went wrong.」が表示されるのでRailsサーバーは起動しており、pumaに到達しているのでNginxの設定には問題なさそうでした。
そこでpumaにあたりをつけて調べてみると、pumaはローカルホストで起動していました。$ ps -alx | grep "puma" puma 4.3.3 (tcp://0.0.0.0:3000)なるほど、pumaの設定ファイルが悪いんだな、ということでconfig/puma.rbを書き換えてあーじゃない、こーじゃないとやっていたのですが、ここで気づきました。
config/puma.rbと同じ階層にpumaフォルダがあるぞ...?中を確認するとproduction.rbファイルが...
そう、本番環境用の設定ファイルを作成していたことをすっかり忘れていたのです。。。解決方法
原因が分かってしまえば簡単な話です。
config/puma/production.rbを編集して解決しました。
ファイルの設定を載せておきます。config/puma/production.rbenvironment "production" # UNIX Socketへのバインド bind "unix://#{Rails.root}/tmp/sockets/puma.sock" # socketの設定 daemonize # デーモン化一応Nginxの設定もpuma関連部分だけ載せておきます。
/etc/nginx/conf.d/app_name.confupstream app_server { server unix:/var/www/rails/app_name/tmp/sockets/puma.sock fail_timeout=0; }これでサーバーを起動したところ、問題なくデーモンで起動しました。
設定ファイルにdaemonizeを追加すると-dをつけなくても自動的にデーモンで起動するようです。便利ですね。$ rails s -e production => Booting Puma => Rails 6.0.2.2 application starting in production => Run `rails server --help` for more startup options Puma starting in single mode... * Version 4.3.3 (ruby 2.7.1-p83), codename: Mysterious Traveller * Min threads: 0, max threads: 16 * Environment: production * Daemonizing...「Daemonizing...」頂きました。
おつかれさまでした。あとがき
こんな初歩的なミスを記事にするかは迷ったのですが、似た記事が見当たらなかったので一応書いてみました。
本番環境でも問題なくrailsサーバーは起動するのに、デーモンでの起動だけうまくいかないと言うのも原因の特定を難しくしていたと思います。
そもそも「rails s -d -e production」のコマンドは正しかったのでしょうか...?
config/puma/production.rbには元々bindの設定は書いていたので上記コマンドでもうまく起動しそうなもんですが...知識が足りないので完全な原因究明とはなっていないのが気になるところですが、それは追々と言うことで...
(勉強しないと。。。)詳しい方いましたら、是非コメントをお願いいたしますm(_ _)m
- 投稿日:2020-07-26T02:39:23+09:00
Rails Simple Calendarで記事投稿後、カレンダーに反映させたい。
Simple Calendarにて
記事投稿→カレンダー投稿画面(create)→カレンダーに反映という事を行いたいのですが、
現状、記事投稿は出来ていて、そこから記事投稿のタイトルデータを抜き出してカレンダー投稿画面にタイトルのみデータを表示取得させたいです。
※こちらの画面ではTitleが入力フォームになっていますがこのTitleを、記事のTitleを取得したいです。1、
- @tweets.each do |tweet|
= @tweet.title
を入力しましたが
このようなエラーが出ました。
blog(カレンダー)とtweet(記事)のアソシエーションとして
はこのように組んでいます。
blogのdb(データベース)はこちらになります。
create_table :blogs do |t| t.string :title t.text :content t.datetime :start_time t.references :user, foreign_key: true t.references :tweet, foreign_key: true t.string :tweet_title t.timestamps endblog_controllerはこちらになります。
ご回答いただければ幸いです。
未熟者ですがよろしくお願いします。
- 投稿日:2020-07-26T02:18:00+09:00
RailsTutorialチートシート
「RailsTutorialをやったはいいが、2週間もしたら何をしたか思い出せなくなる」
「結局自分には何ができるのか」「俺は...弱い...!」
そんな人のためのチートシートです。
とりあえず保存をして、こっそりコピペして、色々付け加えながら自分だけのチートシートを作りましょう。
機能要件
[Rails]検索機能
https://qiita.com/shin1rok/items/779e581e9d12a92310c3
[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
[Rails]投稿した画像が大きくなっちゃう
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
[Rails]ログイン、ログアウト機能(外部ライブラリ- devise)
https://qiita.com/cigalecigales/items/f4274088f20832252374
[Rails]ログインしてブラウザを閉じてもログイン状態を維持したい! - Remember me
https://railstutorial.jp/chapters/advanced_login?version=5.1#sec-remember_me
[Rails]サムネイル画像を入れたい - Gravatar
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非機能要件
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_validationsView系
[Rails]ページネーション(ページ分割)
https://railstutorial.jp/chapters/updating_and_deleting_users?version=5.1#sec-pagination
[Rails]編集フォーム
https://railstutorial.jp/chapters/updating_and_deleting_users?version=5.1#sec-edit_form
[Rails]一覧表示させたい
https://railstutorial.jp/chapters/updating_and_deleting_users?version=5.1#sec-showing_all_users
[Rails]部分テンプレートを呼び出したい → render ‘ファイル名’
https://pikawaka.com/rails/renderController系
[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_actionURL,ルーティング系
[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_formDB,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[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]インデックス
インデックスを作成することでテーブルとは別に検索用に最適化された状態で必要なデータだけがテーブルとは別に保存される。検索用に並び替えをしている上、インデックスを貼ったカラムだけを検索できるので高速検索が可能。デメリットは、テーブルとは別に検索用のテーブルが保存される関係上データの追加に時間がかかること。なぜなら、データを追加するときに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]リレーションの時の別テーブルのデータの呼び出し方
https://railstutorial.jp/chapters/user_microposts?version=5.1#sec-destroying_microposts[Rails]あるユーザーが同じユーザーを2回以上フォローすることを防ぐこと- 複合キーインデックス
https://railstutorial.jp/chapters/following_users?version=5.1#sec-a_problem_with_the_data_modelセキュリティ系
[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エラー系
[Rails]エラーメッセージをブラウザ画面に表示したい
https://railstutorial.jp/chapters/basic_login?version=5.1#sec-rendering_with_a_flash_message
https://railstutorial.jp/chapters/sign_up?version=5.1#sec-signup_error_messages
https://railstutorial.jp/chapters/sign_up?version=5.1#sec-the_flashタイミング系
[Rails]Emailアドレスをデータベースに保存する際に事前に小文字にしておきたい - コールバック
https://qiita.com/okamoto_ryo/items/458097542e826623b7ad
https://railstutorial.jp/chapters/account_activation?version=5.1#sec-activation_token_callback
コールバック。オブジェクトが生成(create)、更新(update)、破壊(delete)される時や、バリデーションを実行する時の前後に共通の処理を追加する仕組みのことを指す
before_save { self.email = email.downcase }
ブロックを渡してユーザーのメールアドレスを設定します
before_create :create_activation_digest
上のコードはメソッド参照と呼ばれるもので、こうするとRailsはcreate_activation_digestというメソッドを探し、ユーザーを作成する前に実行するようになります認可、認証、権限関連
[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/c4e4736f2a924b03777bDRYに書きたい(可読性、保守性)
[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]privateメソッドをcontrollerに定義したが、とても汎用性があるので異なる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
- 投稿日:2020-07-26T00:18:08+09:00
オブジェクトとクラスを理解する
irb
対話的な実行環境
オブジェクト型を確認する
"文字列"(レシーバ).class → String
1.class → Integerobject_idを確認する
"文字列".object_id → 実行されるたび別のオブジェクトが作られる
1.object_id → 同じ数値オブジェクトが提供されるオブジェクトを結合する
message1 = "メッセージ1"
message2 = "メッセージ2"
message1.concat(message2) ※括弧の省略可
message1 = メッセージ1メッセージ2文字列オブジェクト
人間が読むことのできる文字や記号で構成された、単語や文章のようなデータ
ダブルクォーテーションで内容を囲む(シングルクォーテーションでも可)数値オブジェクト
数を表すオブジェクト
クラスとインスタンス
オブジェクトがどんな機能持てるかは、そのオブジェクトがどんなクラスのオブジェクト出るかで変わってくる。Rubyが標準的に用意しているクラス(組み込みライブラリ・標準添付ライブラリ)の他に、自分でクラスを作ることも可能。
変数
abc = "氏名"
→ String命名方法
スネークケース
sample_name
キャメルケース
sampleMessage
※大文字から始まる名前は、保持する値が不変の「定数」と解釈されるコメント
「#コメントアウト」
メソッド
Rubyのオブジェクトの振る舞いは、メソッドとする。
「犬(クラス)のムギ(インスタンス)は人間に嘘をつく(メソッド)能力を持っている」
=ムギ.嘘をつく(人間)
※嘘をつく = インスタンスメソッド犬クラスに追いかけるを定義し、ムギだけでなくどの犬オブジェクトに対しても「嘘をつく」メソッドを呼び出すことができる。
class 犬 def 嘘をつく(人間) puts "犬は#{人間}に嘘をついた..." end end ムギ = 犬.newインスタンス変数
オブジェクトが抱える変数。オブジェクトのどのメソッド内からも利用できる。名前の先頭には必ず@をつける。
class Sample def samplemethod1 @number = 100 #インスタンス変数 end def samplemethod2 @number end end #object = Sample.new #object.samplemethod1もobject.samplemethod2 も呼び出し可能ローカル変数
その場限りの一時的な変数。メソッド内で定義したローカル変数はそのメソッド内でしか使うことができない。
class Sample def samplemethod1 number = 100 #ローカル変数 end def samplemethod2 number end end #object = Sample.new #object.samplemethod1は呼び出せるが、object.samplemethod2 は呼び出せないゲッター・セッター
class User def name=(name) #セッター @name = name end def name #ゲッター @name end end #↓簡単な書き方↓ class User attr_accessor :name, :address, :email end
- 投稿日:2020-07-26T00:18:08+09:00
Ruby入門
irb
対話的な実行環境
オブジェクト型を確認する
"文字列"(レシーバ).class → String
1.class → Integerobject_idを確認する
"文字列".object_id → 実行されるたび別のオブジェクトが作られる
1.object_id → 同じ数値オブジェクトが提供されるオブジェクトを結合する
message1 = "メッセージ1"
message2 = "メッセージ2"
message1.concat(message2) ※括弧の省略可
message1 = メッセージ1メッセージ2文字列オブジェクト
人間が読むことのできる文字や記号で構成された、単語や文章のようなデータ
ダブルクォーテーションで内容を囲む(シングルクォーテーションでも可)数値オブジェクト
数を表すオブジェクト
クラスとインスタンス
オブジェクトがどんな機能持てるかは、そのオブジェクトがどんなクラスのオブジェクト出るかで変わってくる。Rubyが標準的に用意しているクラス(組み込みライブラリ・標準添付ライブラリ)の他に、自分でクラスを作ることも可能。
変数
abc = "氏名"
→ String命名方法
スネークケース
sample_name
キャメルケース
sampleMessage
※大文字から始まる名前は、保持する値が不変の「定数」と解釈されるコメント
「#コメントアウト」
メソッド
Rubyのオブジェクトの振る舞いは、メソッドとする。
「犬(クラス)のムギ(インスタンス)は人間に嘘をつく(メソッド)能力を持っている」
=ムギ.嘘をつく(人間)
※嘘をつく = インスタンスメソッド犬クラスに追いかけるを定義し、ムギだけでなくどの犬オブジェクトに対しても「嘘をつく」メソッドを呼び出すことができる。
class 犬 def 嘘をつく(人間) puts "犬は#{人間}に嘘をついた..." end end ムギ = 犬.newインスタンス変数
オブジェクトが抱える変数。オブジェクトのどのメソッド内からも利用できる。名前の先頭には必ず@をつける。
class Sample def samplemethod1 @number = 100 #インスタンス変数 end def samplemethod2 @number end end #object = Sample.new #object.samplemethod1もobject.samplemethod2 も呼び出し可能ローカル変数
その場限りの一時的な変数。メソッド内で定義したローカル変数はそのメソッド内でしか使うことができない。
class Sample def samplemethod1 number = 100 #ローカル変数 end def samplemethod2 number end end #object = Sample.new #object.samplemethod1は呼び出せるが、object.samplemethod2 は呼び出せないゲッター・セッター
class User def name=(name) #セッター @name = name end def name #ゲッター @name end end #↓簡単な書き方↓ class User attr_accessor :name, :address, :email end演算子
Left align Left align + 足す、文字列の連結、配列の連結 - 引く、配列から一部の要素を削除 * かける、文字列を繰り返し連結、配列を繰り返し連結 / 割る % 割った余りを得る && / and AND演算 ^ XOR演算 ! / not 真偽を裏返す(否定) = 代入 == 等しいか調べる != 等しくないか調べる >, >=, <, <= 左辺が大きい、左辺が右辺以上、右辺が大きい、右辺が左辺以上 nil
空っぽの状態
sample = nil?
nilであるかを調べる真偽
Rubyではnilとfalseが偽、それ以外が真(0も真)。
条件分岐
number = 1 if number == 1 puts '数値は1' elseif number == 2 puts '数値は2' else puts '数値は1と2以外' end#unlessを用いた表現 age = 20 unless age >= 20 puts "投票権がない” end#ifを用いた表現 age = 20 if age < 20 puts "投票権がない” end後置if
puts "これは出力される" if true
puts "これは出力されない" if false配列
複数の要素が順番に格納された構造
array = ["123", false, nil, 1, [a,b,c]]
要素を追加は
array << aarray = [1,2,3] array.each do |element| puts element endclass User attr_accessor end user1 = User.new user1.name = 'mayu' user2 = User.new user2.name = 'ayako' user3 = User.new user3.name = 'kenji' users = [user1, user2, user3] #1つずつ取得したい場合、、 #方法①eachを使う names = [] users.each do |user| names << user.name end p names #==>["mayu", "ayako", "kenji"] #方法②mapを使う names = users.map do |user| user.name end #==>["mayu", "ayako", "kenji"] #方法②の省略① names = users.map{ |user| user.name } #==>["mayu", "ayako", "kenji"] #方法②の省略② names = users.map(&:name) #==>["mayu", "ayako", "kenji"]ハッシュ
内部的にデータをキーと対応づけて格納しておくデータ構造
様々な記法
#文字列をキーとする {"student1" => mayu, "student2" => asami} #文字列をキーとし、ハッシュロケットではなくコロンを使用 {"student1": mayu, "student2": asami} #シンボルをキーとする {:student1 => mayu, student2 => asami} #シンボルをキーと,ハッシュロケットではなくコロンを使用 ※一般的 {student1: mayu, student2: asami}値を取得
array = {:student1 => mayu, student2 => asami} puts array[:student1] #mayuと出力される値を更新
array = {:student1 => mayu, student2 => asami} array[:student1] = 'misaki' puts array[:student1] #misakiと出力される