20200726のRailsに関する記事は15件です。

Gitに管理されたファイルを削除したい

.gitignoreに書き忘れてcommitしてしまった時に、
「.gitignoreに追記すれば大丈夫...」
と誤解していませんか?
僕のように..汗

後から.gitignoreに追記するだけではすでにGitに管理されたファイルは除去されないので、そんな時の対処方法を書いておきます。

Gitに管理されたファイルを残して除外したい場合

$ git rm --cached [除外したいファイル名]

その後は必ず.gitignoreに除外したいファイルを追記する。
注意箇所:
--cachedを必ずつける!!
つけないとファイルごと削除してしまいます。

ファイルごと削除したい場合

$ git rm [削除したいファイル名]

ディレクトリごと削除したい場合

$ git rm -r [削除したいディレクトリ]

最後に

Gitを使い始めた時に何回か忘れてcommitして、都度調べていたので備忘録として残しておきます。

同じ境遇になった方のお役に立てば嬉しいです。

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

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 s

http://localhost:3000 にアクセスして馴染みの画像が出ていればOK。

スクリーンショット 2020-07-26 21.55.06.png

modelとcontrollerを作成

今回はpostという名前で。

$ bin/rails g model post name:string 
$ bin/rails db:migrate
$ bin/rails g controller posts

ルーティング

config/routes.rb
Rails.application.routes.draw do
  resources :posts
end

Active 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.rb
class Post < ApplicationRecord
  has_many_attached :images
end

controllerを記述

controllers/posts_controller.rb
class 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
end

viewを作成

投稿一覧ページ

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 にアクセス

新規投稿から画像を複数投稿する事ができた。

スクリーンショット 2020-07-26 20.51.02.png

任意の画像を削除する

controllerにメソッド追記

編集に必要なedit、updateメソッドを追記します。

controllers/posts_controllers.rb
  def 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-26 21.35.14.png

任意の画像だけ削除できました。

スクリーンショット 2020-07-26 21.36.05.png

参考

ActiveStorageを使って複数画像管理をしてみる
ActiveStorage で画像を複数枚削除する方法

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

タスク管理 アプリケーション作成手順

1. アプリケーション作成準備

1-1. 作成するアプリケーションの内容を考える

機能     機能内容
一覧表示    全てのタスクを一覧表示する
詳細表示       一つのタスクの詳細内容を表示する
新規登録       新しいタスクをdbに登録する
編集         登録済みのタスクを編集し、dbを更新する
削除         登録済みのタスクをdbから削除

1-2. アプリケーション名を決め、雛形を作成

rails new アプリケーション名 [オプション]

#例)

rails new taskmanage -d postgresql

1-3. dbを作成する

アプリケーションを作成したディレクトリに移動しdbを作成する

rails db:create

1-4. サーバーを起動してみる

以下のコマンドを叩き、
http://localhost:3000」
にアクセスし、railsのデフォルトページが表示されるか確認

rails s

2. モデルを作成

モデルの構成要素

  • モデルに対応するRubyのクラス
    キャメルケース

  • モデルに対応するデータベースのテーブル
    モデルのクラス名を複数名にしたもの、スネークケース

2-1. モデルの属性を設計する

属性  属性名 データ型 nullを許可するか デフォルト値
名称   name string   許可しない   なし  
詳しい説明   description text   許可する   なし  

2-2. モデルの雛形を作成

rails g model [モデル名] [属性名:データ型 属性名:データ型 ...] [オプション]

#例)

rails g model Task name:string description:text

2-3. マイグレーションをし、dbにテーブルを追加

作成されたマイグレーションファイルを確認し、rails db:migrateを実行

3. コントローラーとビューの作成

rails g controller コントローラー名 [アクション名 アクション名...] [オプション]

#例)

rails g controller tasks index show new edit
Rails.application.routes.draw do
 root to: 'tasks#index'
 resources :tasks
end

3. 新規登録機能の実装

3-1. 一覧画面に新規登録ページへ遷移するリンクを追加

= link_to '新規登録', new_task_path

3-2. 新規登録画面のためのアクションを実装

tasks_controller.rb
def new
 @task = Task.new
end

3-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

3-4. 登録アクションの実装

createアクション = 「登録フォームから送られてきたデータをdbに保存し、一覧画面に遷移」

tasks_controller.rb
def 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)
 end

renderとRedirect_toの違い

render Redirect_to
アクションに続けてビューを表示させる アクションを処理した直後にビューを表示せず、別のURLに案内する

flashメッセージ

リダイレクト時に、次のリクエストに対してちょっとしたデータを伝える

 redirect_to tasks_url, notice:"タスク「#{task.name}」を登録しました。"
#タスク登録完了メッセージをしてからリダイレクトするという意味

flash.now[:alert] = "すぐにアラート"
#同じリクエスト内の場合は、flash.now

4. 一覧表示機能の実装

4-1. 新規登録画面のためのアクションを実装

tasks_controller.rb
def index
 @tasks = Task.all
end

4-2. 一覧ページで全てのタスクを表示

@tasks.each do |task|
 task.name
 task.created_at

5. 詳細表示機能の実装

5-1. 一覧ページから詳細ページへのリンクを追加

index.html.slim
link_to task.name, task_path(task)

5-2. 選択されたタスクを詳細表示アクションで取得

tasks_controller.rb
def show
 @task = Task.find(params[:id])
end

5-3. 選択されたタスクを詳細表示アクションで取得

show.html.slim
@task.id
@task.name
@task.created_at
@task.updated_at

6. 編集機能の実装

6-1. 一覧ページと詳細ページに編集ページへのリンクを追加

index.html.slim
link_to '編集', edit_task_path(task)
show.html.slim
link_to '編集', edit_task_path

6-2. editアクションとupdateアクションを定義づける

tasks_controller.rb
def 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}」を更新しました。"
end

6-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.rb
def destroy
 task = Task.find(params[:id])
 task.destroy
 redirect_to tasks_url, notice: "タスク「#{task.name}」を削除しました。"
end

参考|現場で使える Ruby on Rails 5速習実践ガイド

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

【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.yml
version: "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」で保存をして終了してください。
Dockerfile
FROM 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=utf8mb4
terminal
# 最後に 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」で保存をして終了してください。
Dockerfile
FROM 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.yml
default: &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-26 16.49.15.png

以上でサンプルサイトの環境構築終了です!

■ 終わりに

初心者の方だけでなく、経験者の方でもあっても、初めて触る技術の学習にはかなり苦戦すると思います。
そのため、先に「どういうサイトがどのように動いているか」ということを知るだけでも学習効率は上がるんじゃないかなーと思い、今回の作業に取り掛かりました。
まだ私も環境構築をしてすぐにこの記事を書いているので、この環境を使った学習は始めていませんが、実際にログを仕込んでみたりしながら体感的に学ぶ方が飲み込みは早いと考えるので、ガンガンこの環境とこのサイトを元に学習に取り組んでいこうと思います。

この記事は解説部分を完全に端折って、ただただ完成させることだけを目標に書き上げました!
そのため、しっかりと解説を確認したい方は、参考文献様の記事をご確認いただいて、照らし合わせながら進めてほしいと思います。

僕「本当に先人の方々はすげえや。」

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

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 --system

RubyGemsのバージョンアップを実行。そして

$ bundle update --bundler

bundlerのアップグレートを実行。

最後にrails serverを実行したら

Please run rails webpacker:install

と出たので

$ bundle exec rails webpacker:install

を実行することでサーバーの起動が確認できた。

スクリーンショット 2020-07-26 15.35.06.png

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

ランキング機能のつくりかた【Railsの内部結合と外部結合を理解する】

概要

この記事は、実際に私がBooQsで運用している「ランキング機能」を例にして、Railsのテーブル結合(内部結合と外部結合)を解説する記事です。

前提条件

Rails 5.1

【?‍♂️Before】テーブル結合を使わないランキング機能

私が開発しているBooQsという英単語学習サービスでは、ゲーミフィケーションを用いてユーザーの学習意欲を高めるために、英単語の解答数(userのもつanswer_historiesの数)によるユーザーのランキング機能を実装しています。

スクリーンショット 2020-07-25 12.00.25.png

しかし、以下のNewrelicのダッシュボードを見ていただくとわかるように、自分の書いたランキング機能の処理速度はお世辞にも良いものとは言えませんでした。

88397035-546c6a80-cdfe-11ea-86a2-84e98d80bd51.png

それではまず、自分が組んだ(下手くそな)コードから見ていきましょう。
恥ずかしながら、自分はテーブル結合をよく理解していなかったため、次のようなまどろっこしいコードでランキング機能を実装していました。

home_controller.rb
def 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.rb
class User < ApplicationRecord
  has_many :answer_histories, dependent: :destroy
end
models/answer_history.rb
class AnswerHistory < ApplicationRecord
  # 非ログインユーザーの解答記録は、user_idをnilで記録する。
  belongs_to :user, optional: true
end

View

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: '&#8592; &nbsp;前へ', next_label: '次へ &#8594;',
                        page_links: false %>

    </div>

  </div>

</div>

実際のランキングページは以下になります。

ランキングページ: https://www.booqs.net/home/user_ranking

【?‍♂️After】内部結合を使ったランキング機能

Beforeのテーブル結合を利用しない方法だと、user_idsを取得するためにクエリを発行し、さらにユーザーを取得するたびに、user = User.find(id)でクエリが発行されるため、処理が遅くなります。

そのため、知り合いのベテランの開発者の方に、下のような方法をオススメいただきました。

home_controller.rb
def 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つがありますね!

  1. answer_historiesのuser_id(外部キー)の数だけ山田さんと清水さんが増殖した。
  2. answer_historiesのuser_idに存在しない長谷川さんは排除された。
  3. user_idがnilであるanswer_historiesのidが2と7のレコードが排除された

注意:例ではanswer_historiesの主キー(id)の昇順でレコードを並べていますが、私の調べた限りでは、結合結果のレコードの順序については、特定のidを基準にした昇順・降順などはないようでした。もし私が間違っていたら誰かぜひ教えてください。)

 
 

このようなjoinsメソッドを利用したテーブル同士の結合方法は、【内部結合】と呼ばれます。

内部結合では、結合条件に合致しないレコードは排除されます。

内部結合の結合条件とは、結合先の外部キー = 結合元の主キーです。

BooQsの例で紹介しましょう。
たとえば、今回のUser.joins(:answer_histories)によって内部結合を行うと、次のようなSQLが発行されます。

「内部結合」で発行されるSQL
SELECT  "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種類のレコードが排除されていることがわかるはずです。

  1. answer_historiesの外部キー(user_id)に存在しないusersレコード(例:長谷川さんのレコードが排除されている)
  2. 外部キーが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における「左外部結合」のやり方.rb
User.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における「右外部結合」のやり方.rb
User.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のクエリは、次のようになっています。

ランキング順位で並べ替え時に発行されるSQL
SELECT  "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" でなくてはならないのでしょうか??

何卒ご教示いただければ幸いです...!?‍♂️

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

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 の対応待ちなのかな…

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

Rails基本 処理の流れ

railsの処理の流れ

user/indexをリクエストした場合

Screen Shot 2020-07-26 at 13.27.24.png

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

gem 'kaminari' でページネーション実装

準備

  • Gemfileに'kaminari'追加
gem 'kaminari'
  • bundle install

実装例

  • Itemモデルから1回の検索で50レコード取得する
Item.all.page(1).per(50)
  • 配列に対して実装する場合
1ページに対して10レコードずつ表示する  
Kaminari.paginate_array(array).page(params[1]).per(10)  
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Ruby入門2

initialize

newメソッドでオブジェクトを生成するとき、そのオブジェクトのinitializeメソッドが実行される

class User
 attr_reader :name, :address, :email
 def initialize(name, address, email)
  @name = name
  @address = address
  @email = email
 end 
end

private

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」を使い、まとめてクラスに取り込んでもらうことができる。
Screen Shot 2020-07-26 at 12.21.19.png

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

例外捕捉

  1. 例外が発生するかもしれないコードをbeginのなかに記述
  2. その中で発生した例外への対応の仕方をrescueの中に記述
  3. さらに、例外が出た場合も出なかった場合も必ず行いたい後処理をensureののなかに記述
begin
 #例外が発生するかもしれないコード
rescue
 #例外に対応するコード
ensure
 #例外が発生してもしなくても必ず実行したいコード
end

nilガード

もし変数が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]

参考|現場で使える Ruby on Rails 5速習実践ガイド

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

【Rails+EC2+puma】rails s -e productionは問題ないのに、rails s -dで起動するとエラーになる

環境

Rails: 6.0.2.2
ruby: 2.7.1
Puma: 4.3.5

EC2+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.rb
environment "production"

# UNIX Socketへのバインド
bind "unix://#{Rails.root}/tmp/sockets/puma.sock" # socketの設定
daemonize # デーモン化

一応Nginxの設定もpuma関連部分だけ載せておきます。

/etc/nginx/conf.d/app_name.conf
upstream 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

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

Rails Simple Calendarで記事投稿後、カレンダーに反映させたい。

Simple Calendarにて
記事投稿→カレンダー投稿画面(create)→カレンダーに反映という事を行いたいのですが、
現状、記事投稿は出来ていて、そこから記事投稿のタイトルデータを抜き出してカレンダー投稿画面にタイトルのみデータを表示取得させたいです。
540d264cf48615032c7353a06dffbdf6.png
※こちらの画面ではTitleが入力フォームになっていますがこのTitleを、記事のTitleを取得したいです。

試した事ですが
5336e6a17d83d2db786feb63021d995c.png

1、
- @tweets.each do |tweet|
= @tweet.title
を入力しましたが
d2cf2caa748b7b3ad991e29b02e302bb.png

このようなエラーが出ました。

blog(カレンダー)とtweet(記事)のアソシエーションとして
4c606c184b85049ae3185dc556a08022.png
025a6b6e9003a3707599aa75945c6d5e.png

はこのように組んでいます。

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
end

blog_controllerはこちらになります。

19b8126718b90974d1a4db94d7d11982.png

記事投稿の際に他のページだとビューの表示は出来ました。
52b32613dc3a1aa18170323ae8d61abe.png
db9032b4f568d364a6a228d3c5415f0b.png

ご回答いただければ幸いです。
未熟者ですがよろしくお願いします。

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

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_validations

View系

[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/render

Controller系

[Rails]フォームの入力に失敗した時の実装
https://railstutorial.jp/chapters/updating_and_deleting_users?version=5.1#sec-unsuccessful_edits
[Rails]Railsのredirect_toにおけるpathとurlの使い分け(同じようなもの)
https://teratail.com/questions/204077
[Rails]部分テンプレートを呼び出したい → render ‘ファイル名’
https://pikawaka.com/rails/render
[Rails]Destroyアクションとセキュリティ
https://railstutorial.jp/chapters/updating_and_deleting_users?version=5.1#sec-the_destroy_action
[Rails]newとbuildの違い
newメソッドとbuildメソッドはともにインスタンスを生成するが、
buildは自動的にuser_idをセットしてインスタンスを生成することができる。
https://qiita.com/Kaisyou/items/8876f39e12631f4e5154
[Rails]CRUD(Create)
https://railstutorial.jp/chapters/sign_up?version=5.1#sec-signup_form
[Rails]CRUD(Read)
https://railstutorial.jp/chapters/updating_and_deleting_users?version=5.1#sec-showing_all_users
[Rails]CRUD(Destroy)
https://railstutorial.jp/chapters/updating_and_deleting_users?version=5.1#sec-the_destroy_action

URL,ルーティング系

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

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

[Rails]resourceを使ったRESTfulなルーティングにRESTful以外の新しいアクションを追加したい。
https://qiita.com/ebihara99999/items/37afb1486442e7c16a8a
https://railstutorial.jp/chapters/following_users?version=5.1#sec-stats_and_a_follow_form

DB,ActiveRecord系

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

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

[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/c4e4736f2a924b03777b

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

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

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

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

[Rails]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

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

オブジェクトとクラスを理解する

irb

対話的な実行環境

オブジェクト型を確認する

 "文字列"(レシーバ).class → String
 1.class → Integer

object_idを確認する

 "文字列".object_id → 実行されるたび別のオブジェクトが作られる
 1.object_id → 同じ数値オブジェクトが提供される

オブジェクトを結合する

 message1 = "メッセージ1"
 message2 = "メッセージ2"
 message1.concat(message2) ※括弧の省略可
 message1 = メッセージ1メッセージ2

文字列オブジェクト

人間が読むことのできる文字や記号で構成された、単語や文章のようなデータ
ダブルクォーテーションで内容を囲む(シングルクォーテーションでも可)

数値オブジェクト

数を表すオブジェクト

クラスとインスタンス

オブジェクトがどんな機能持てるかは、そのオブジェクトがどんなクラスのオブジェクト出るかで変わってくる。Rubyが標準的に用意しているクラス(組み込みライブラリ・標準添付ライブラリ)の他に、自分でクラスを作ることも可能。

Screen Shot 2020-07-25 at 22.42.22.png

Screen Shot 2020-07-26 at 0.15.32.png

変数

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
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Ruby入門

irb

対話的な実行環境

オブジェクト型を確認する

 "文字列"(レシーバ).class → String
 1.class → Integer

object_idを確認する

 "文字列".object_id → 実行されるたび別のオブジェクトが作られる
 1.object_id → 同じ数値オブジェクトが提供される

オブジェクトを結合する

 message1 = "メッセージ1"
 message2 = "メッセージ2"
 message1.concat(message2) ※括弧の省略可
 message1 = メッセージ1メッセージ2

文字列オブジェクト

人間が読むことのできる文字や記号で構成された、単語や文章のようなデータ
ダブルクォーテーションで内容を囲む(シングルクォーテーションでも可)

数値オブジェクト

数を表すオブジェクト

クラスとインスタンス

オブジェクトがどんな機能持てるかは、そのオブジェクトがどんなクラスのオブジェクト出るかで変わってくる。Rubyが標準的に用意しているクラス(組み込みライブラリ・標準添付ライブラリ)の他に、自分でクラスを作ることも可能。

Screen Shot 2020-07-25 at 22.42.22.png

Screen Shot 2020-07-26 at 0.15.32.png

変数

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 << a

array = [1,2,3]
array.each do |element|
 puts element
end
class 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と出力される

参考|現場で使える Ruby on Rails 5速習実践ガイド

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