20201207のRailsに関する記事は30件です。

flashメソッドの角括弧の中身はなんでもいい件

はじめに

 先日、flashメソッドを使って、メッセージ送信成功時に画面に「成功しました」的なメッセージを表示させる方法を投稿したが、その後に調べていると驚くべき発見があったので、紹介。

前回の投稿では

コントローラーとビューに以下のように記述するように紹介した。

flash[:success] = '送信されました'
<%= flash[:success] %>

そのときは、:successに意味があると思っていた。しかし、:successには特別な意味がなかった。その時には、他にも:noticeを角括弧で入れている場合もあり、
成功時には、:success
失敗時には、:noticeと思い込んでいた。

実は

 角括弧の中身は、コントローラーとビューが一致さえしていれば、どんな文字でもOK!もちろん、:aでもいい。(まぁ、わかりやすい命名の方が読みやすいが…)
 あくまで、ハッシュのような形で、それをビューで表示させているようだった。

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

ポートフォリオ作成記録 第4章の進捗

カスタムヘルパーを作ろう

ヘルパー関数はC#やC++での学習でも出会ったことがあります。(ヘルパー関数は他の関数の処理を助ける便利な関数と覚えた記憶があります。よし!この場合の私は、このことはちゃんと復習が必要ですね!読み流してください。笑)
チュートリアルの内容だと組込関数以外で自作の関数を指しているみたいです。

Railsでインタプリタ(逐次翻訳)を体験

「rails console」でインタプリタモードになるので、Rubyを練習する事ができます。あくまでも、簡単なプログラムを組んで試す事ができるので、その時は便利かも知れません。チュートリアルでは文字列操作でRubyの変数を展開(ダブルクオートの中で#{変数}を使うと変数が組み込める、文字列は、+の記号で文字列を結合するのと同じですね!)

Rubyのクオートで処理結果が異なる

インタプリタでRuby言語の記述を学んで文字列のクオートの意味と動作違うのでメモです。
シングルの文字列だと変数展開(#{})ができない。

今日から進んだ所までを投稿しようと思います

これまでは、章が完了する事に投稿しようと思いましたが、復習の意味もこめてその都度、記述して投稿しようと思います。また、不明な言葉や内容の部分も記録してスクラップ記事みたいに集めて2周目の復習時に調べて行うのにいいのかとも思っています。色々、試行錯誤して投稿していこうと思います。

自分が知っている専門用語

目的=今回は自分の知っている用語をテストの意味で残すのが目的です。確認するのが楽しみ!
・組込関数
他のクラスを呼び出して使わない関数のこと。
呼び出せる理由は、コンピュータ向けの言語に変換されているファイルをプログラム実行時に組み込ませているので(リンカ)記述が不要。Cのビルドの工程を見ると知れます。興味があれば調べてどうぞ!
知らないと、組込エンジニアがドヤってくるよ!(私の体験談です。笑)

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

【備忘録】deviseの導入

deviseとは?

Railsアプリケーションに認証機能(ユーザー登録機能、ログイン機能、ログアウト機能)を実装することができるgemです。数回のコマンドを実行するだけで簡単に認証機能を実装することができます。

導入の流れ

1. Gem導入

Gemfileに追記。

Gemfile.
gem 'devise'

bundle install実行。

bundle install

2. インストール

アプリケーションにdeviseをインストール。ここは忘れがちなので注意!

rails g devise:install

3. Userモデルの作成

rails g devise user

モデル作成と同時に、マイグレーションファイルやルーティングも自動的に作成されます。

4. 必要に応じてカラムを追加

deviseを介してモデルを作成した場合、emailとpasswordはデフォルトで用意されているので、他に必要なカラムがある時は追加します。

5. マイグレーション実行

マイグレーションファイルの内容をテーブルに反映させます。

rails db:migrate

6. ビューの作成

新規登録・ログイン用のビューを作成します。

rails g devise:views

こんな感じのビューが出来上がります⬇️

新規登録画面

Image from Gyazo

ログイン画面

Image from Gyazo

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

いいね機能を非同期で実装する方法

はじめに

前回いいね機能を実装しました。
ただ、いいねをするたびにページがリダイレクトされてしまいます。
そこで非同期通信という通信方法を使い、リダイレクトすることなくにビューに反映できるようにしていきたいと思います。
少しだけjQueryの知識が必要になります。

こんな人に向けて

1.いいね機能を実装済みの人。
2.非同期通信での実装をしたい人。

1.前提条件

・いいね機能を実装済みであること(実装していない人は こちら
(前回のいいね機能を改良していきます)

2.実装準備

非同期通信を実装するにあたって、新たなgemをインストールする必要があります。

Gemfile
gem "jquery-rails"
ターミナル
bundle install
app/assets/javascripts/application.js
//= require jquery   (←この行を新しく記載)
//= require rails-ujs 

jQueryというJavaScriptで作成されたライブラリを使うので、上記の方法でインストールしてください。

これで準備完了です。さっそく実装していきます。

3.非同期通信でのいいね機能の実装

3-1.部分テンプレートの作成

前回は投稿一覧画面のみにいいね機能をつけました。
ただ実際は投稿の詳細画面やマイページにも機能をつけると思います。
ビューごとに毎回同じ記述をしても動きますが、非同期通信をするときに大変になります。
なので部分テンプレートを作成してまとめます。

作成方法を
①部分テンプレートのファイルをつくる
②ファイル内にコードを書き写す
③作成したファイルをビューに反映させる
の3つに分けて説明します。

3-1-1.部分テンプレートのファイルをつくる

作成場所はapp/views下の各フォルダ内であればどこでも大丈夫です。
app/views/postsの中でもapp/views/layoutsの中でも動きます。
ただ今回はいいね機能ということでapp/views/favoritesの中に作ります。(favoritesフォルダは前回の2-2の作業で自動作成されます。)
各フォルダにカーソルを合わせ右クリックでファイルの新規作成をし、_favorite.html.erb と名前をつけます。
(先頭にアンダーバーをつけて .html.erb で終わればなんでもOKですが、何の部分テンプレートかファイル名で分かるのがベター。)

3-1-2.ファイル内にコードを書き写す

作成したファイルにコードを書いていきます。
前回の2-7で書いたコードを利用しましょう。

app/views/favorites/_favorite.html.erb
<% if post.favo?(current_user) %>
  <%= link_to favorites_path(post), method: :delete do %>
    ♥ <%= post.favorites.count %>
  <% end %>
<% else %>
  <%= link_to favorites_path(post), method: :post do %>
    ♡ <%= post.favorites.count %>
  <% end %>
<% end %>

いいね機能のみを部分テンプレート化するので、each文は除きました。
(今回の説明では登場しませんが、部分テンプレート内にインスタンス変数(例:@post)がある場合、ローカル変数(例:post)に書き換える必要があります。)

3-1-3.作成したファイルをビューに反映させる

app/views/posts/index.html.erb
<% @posts.each do |post| %>
  <%= render "favorites/favorite", post: post %>
<% end %>

上記のように書きます。

<%= render "favorites/favorite", post: post %>

分解して説明すると、
まずrenderを使って特定のファイルを呼び出します。

その特定のファイルというのが "favorites/favorite" で指定しているものです。
(app/views/favorites/_favorite.html.erbを作成しましたが、views以下のフォルダ及びファイル名の2つをここに記述しています。その際にアンダーバーは記述しません。)

そして最後に post: postで変換作業をしています。
(②の最後に説明しましたが、②でインスタンス変数をローカル変数に変えた場合、ローカル変数を再度インスタンス変数に戻す作業をここでします。その際は

<%= render "favorites/favorite", post: @post %>

と記述します。今回は post: post としてください。)

これで部分テンプレートの完成です。

3-2.ビューを非同期化し、コントローラを変える

app/views/favorites/_favorite.html.erb
<% if post.favo?(current_user) %>
  <%= link_to favorites_path(post), method: :delete, remote: true do %>
    ♥ <%= post.favorites.count %>
  <% end %>
<% else %>
  <%= link_to favorites_path(post), method: :post, remote: true do %>
    ♡ <%= post.favorites.count %>
  <% end %>
<% end %>
app/controllers/favorites_controller.rb
  def create
    post = Post.find(params[:post_id])
    favorite = Favorites.new(post_id: post.id)
    favorite.user_id = current_user.id
    favorite.save
  end

  def destroy
    post = Post.find(params[:post_id])
    favorite = current_user.favorites.find_by(post_id: post.id)
    favorite.destroy
  end

link_toremote: trueを付け加えて非同期化しています。
また、コントローラのredirect_to request.refererを消しています。

今まではリンクが押された際にfavorites_controller.rbのcreateアクションまたはdestroyアクションに処理がとび、redirect_toのもとページがリクエストされていました。

それがビューにremote: trueを付け加え、redirect_toを消したことにより、次のリクエスト先がjsファイルに変わります。
jsとはjavascriptのことですが、少し説明したjQueryを使います。

3-3.jsファイルの作成

ここではリクエスト先のjsファイルをつくっていきます。

部分テンプレートの時と異なり、特定のフォルダ下に作成します。
今回はfavoritesコントローラから飛ぶリクエスト先なので、app/views/favorites下にcreate.js.erbdestroy.js.erbをつくります。
(ファイル名はアクション名と同じものにします。like.js.erbなどとファイル名を異なったものにするとエラーが起きます。)
jsファイル内に記述していく前にビューを整えていきます。

3-3-1.ビューのセレクタ設定

jsファイル内で処理を記述していきますが、cssのように「どこの要素に何の処理をするか」と指示します。
この「どこの要素に」ですが少し工夫をする必要があります。
例えば下記のようにしてcssセレクタを設定するとします。

app/views/posts/index.html.erb
<% @posts.each do |post| %>
  <div id="iine"><%= render "favorites/favorite", post: post %></div>
<% end %>

こうしてしまうと仮に1つのいいねボタンを押したときに、全てのいいねボタンが反応してしまいます。

それを防ぐために既にあるeachを利用して、投稿ごとにcssセレクタを設定できるようにします。

app/views/posts/index.html.erb
<% @posts.each do |post| %>
  <div id="post_<%= post.id %>"><%= render "favorites/favorite", post: post %></div>
<% end %>

<%= post.id %>の部分が投稿によって変わるようにしました。
これでいいねボタンが個別のものとして認識されるようになります。

3-3-2.jsファイルの記述

それではjsファイルを記述していきます。

app/views/favorites/create.js.erb
$("#post_<%= @post.id %>").html("<%= j(render 'favorites/favorite', post: @post) %>")

jQueryの書き方は上記のような形になります。

$("#post_<%= @post.id %>")はどこを変えるかという記述になっており、cssとあまり変わりません。
仮に<div class="post_<%= post.id %>">とあれば$(".post_<%= @post.id %>")となります。

.html("<%= j(render 'favorites/favorite', post: @post) %>")でどう変えるかを指定しています。

分解して説明します。
.html()で先ほど指定した部分の中身を変えることを宣言しています。
j()は部分テンプレートをjsファイルに読み込む際に使うものです。escape_javascript()と書かれているものもありますが同じ意味です。
render 'favorites/favorite', post: @postは3-1で説明したとおりです。

destroyも同様に記述します。

app/views/favorites/destroy.js.erb
$("#post_<%= @post.id %>").html("<%= j(render 'favorites/favorite', post: @post) %>")

3-4.コントローラの追記

最後にコントローラ内に記述を加えて完成です。

3-3で作成したjsファイルですがこのままは使えません。
理由はfavoriteコントローラ内で@postが定義されていないからです。

$("#post_<%= @post.id %>")ってあるけど、@postって何?」とエラーが出ます。

中身はjsですがビューと同じなので、コントローラ内で変数定義をしなければ使うことができません。
またビューで使うものなのでローカル変数ではなくインスタンス変数で記述する必要があります。

app/controllers/favorites_controller.rb
  def create
    @post = Post.find(params[:post_id])
    post = Post.find(params[:post_id])
    favorite = Favorites.new(post_id: post.id)
    favorite.user_id = current_user.id
    favorite.save
  end

  def destroy
    @post = Post.find(params[:post_id])
    post = Post.find(params[:post_id])
    favorite = current_user.favorites.find_by(post_id: post.id)
    favorite.destroy
  end

これで完成です。

4.終わりに

これでいいね機能の実装は完成しました。
コメント機能も同じような考え方でできます。
異なる点はいいね機能ではlink_toを使いましたが、コメント機能はform_withを使うことです。
少しだけややこしくなりますが、作る流れは基本的に一緒だと思います。

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

rails Docker対処法

#間違い
docker-compose build 
# 正解
docker-compose up --build  

#間違い
docker rmi sample-rails-docker_app
#正解
docker rmi 91b0abfeb981
Dockerイメージを削除するときはリポジトリではなく、イメージIDで削除する。
katoatsushi@MacBook-Pro sample-rails-docker % docker images                                                              
REPOSITORY                TAG                 IMAGE ID            CREATED             SIZE
sample-rails-docker_app   latest              1cb13ce83e34        4 minutes ago       969MB
<none>                    <none>              424f83731ce0        2 hours ago         969MB
<none>                    <none>              91dde8024805        2 hours ago         969MB
room_app_app              latest              4dcc6beedcfd        5 days ago          989MB
<none>                    <none>              9b3dc2b6e276        5 days ago          989MB
<none>                    <none>              e1de5c89d92e        5 days ago          897MB
<none>                    <none>              f94f1829487e        5 days ago          897MB
myapp_web                 latest              ec1173f0bb40        5 days ago          969MB
docker_sample_web         latest              db6e88beef89        5 days ago          989MB
mysql                     5.7                 ae0658fdbad5        2 weeks ago         449MB
mysql                     8.0                 dd7265748b5d        2 weeks ago         545MB
ruby                      2.6                 39853018958e        2 weeks ago         840MB
ruby                      2.6.5               ad10dfbc638b        8 months ago        840MB
ruby                      2.6.1               99ef552a6db8        21 months ago       876MB
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

RailsエンジニアがGraphQLと比較してみた

はじめに

こんにちは!榊原です。
トレタに入社して1年が経ちました。
この1年はとても変化が多くあった年だったと感じています。

特に今年の1月から会社全体でフルリモート化や、
DXを意識した飲食店での自分のスマホでオーダーできるシステムや予約時に座席を指定して予約できるシステムなど、
時代の変化に対応した様々な新規サービスを発表した年でもあります。

発表したサービスの中にはまだ市場に受け入れられるか仮設段階のものも数多くありました。
エンジニアの人数も限りがある中で、最速かつ柔軟な変化を求められる開発を進めるに当たって、
最近弊社で使われ始めているのがGraphQLです。

今回はそんなGraphQLを弊社でも創業当初から運用されてきたRuby on Railsとの比較を中心とした説明とさせていただきます。

GraphQLを説明をするに当たって、グラフ理論は欠かせないのですがそちらは他の記事が多数存在するため今回は割愛させていただきます。

対象となる読者

  • Railsは使用していてもGraphQLは触ったことがないけど触ってみたい
  • サーバーサイドを担当していたけど、業務で使用しなければならなくなった

対象クライアント、バージョン

GraphQLって何者なの?

GraphQLはFacebookによって開発されました。
元々モバイルアプリはWeb用のラッパーアプリでした。
しかしRESTfulAPIサーバーとFQL(Facebook用のSQL)のデータテーブルで運用されてましたが、
パフォーマンスの低下や度々クラッシュするなど改善が求められました。
そこでFacebookのクライアント、サーバーアプリケーションの性能上のかだいと、データ構造の要件を満たす解決策として誕生したのがGraphQLです。
そして現在ではFacebookのほぼ全てのデータ取得にGraphQLが用いられています。

GraphQLのクライアントアプリケーションとしては、 Relay, Apollo, そして最近ではHasura等があります。

初めの一歩 (GraphQLでできること)

シンプルなGraphQLの記述例ですが、
例えばRailsでPersonsテーブルの情報全て取得するAPIがあるとします

Rails

def PersonsController
  def index
    data = Person.all
    render json: data
  end
end

この場合GraphQLでは下記の様なクエリを記述するだけで取得できます

GraphQL

query {
  persons {
    id
    name
    birthday
    created_at
    updated_at
  }
}

出力結果はGraphQLでは data のハッシュ内に梱包される形になりますが、下記の様な形式で出力されます。

{
  "data": {
    "persons": [
      {
        "id": 1,
        "name": "佐藤太郎",
        "birthday": "2000-01-01",
        "created_at": "2020-12-05T06:31:43.250885+00:00",
        "updated_at": "2020-12-05T06:31:43.250885+00:00"
      },

      〜 略 〜

      {
        "id": 4,
        "name": "伊藤花子",
        "birthday": "2010-03-03",
        "created_at": "2020-12-05T06:32:37.952758+00:00",
        "updated_at": "2020-12-05T06:32:37.952758+00:00"
      }
    ]
  }
}

この様にGraphQLはクエリ言語なため、RailsではあったModelやController、Render等を省き、
簡単なコードだけででRESTFullなAPIを作成することができます。

この後のRailsのサンプルコードとしては、Controllerや render は極力記述せず、ActiveRecordを用いたデータを中心とした記述とします。

使用するSchemaの説明

schema.png

今回使用するのは下記の3つのテーブルで、単純なSNSを想定して作成しました。
- persons: ユーザー
- photos: ユーザーが投稿した写真
- photo_person_tags: 写真にタグ付けされた人

シンプルなQuery

それでは先ほどのクエリを見ていきましょう。

query {       // Select句
  persons {   // テーブル名
    id        // カラム名 (以下同一)
    name
    birthday
    created_at
    updated_at
  }
}

GraphQLではCQRSを採用しているため、最初にQuery(Select句)は query を、Command(Create、Update、Delete句)は mutation を記述します。
今回はSelectなので query を使用しました。

次に、取得したいテーブル名とカラム名を記述すれば、先程の様な値が取得できます。

条件のあるQuery

全てを取得することはできても、条件に合ったもののみ取得したいケースは勿論あるでしょう。
その場合は下記の様に記述します。
名前に佐藤という文字が入っている人を検索します。

Rails

persons.where('name like ?','佐藤%').limit(2)

GraphQL

query {
  persons(where: {name: {_like: "佐藤%"}}, limit: 2) {
    id
    name
    birthday
    created_at
    updated_at
  }
}

persons の中にwhere句、Limit句が追加されただけですね。
基本的な表現は同じです。
条件式に使用できるものとしては下記で確認してください。

https://hasura.io/docs/1.0/graphql/core/queries/query-filters.html

複数のテーブルに対するQuery

実際にサービスを作成していると複数のテーブル情報を一度に取得したいというケースは多くの場面で遭遇すると思います。

ここでは、シンプルに personsphotos のデータを1つのJSON形式で出力してみます。

Rails

persons = persons.all
photos = photos.all

data = {
  persons: persons, 
  photos: photos
}

GraphQL

query {
  persons {
    id
    name
    birthday
    created_at
    updated_at
  }
  photos {
    id
    person_id
    url
    created_at
    updated_at
  }
}

出力結果

{
  "data": {
    "persons": [
      {
        "id": 1,
        "name": "佐藤太郎",
        "birthday": "2000-01-01",
        "created_at": "2020-12-05T06:31:43.250885+00:00",
        "updated_at": "2020-12-05T06:31:43.250885+00:00"
      },

      〜 略 〜

      {
        "id": 3,
        "name": "佐藤花子",
        "birthday": "2010-03-03",
        "created_at": "2020-12-05T06:32:37.249905+00:00",
        "updated_at": "2020-12-05T08:21:50.992633+00:00"
      }
    ],
    "photos": [
      {
        "id": 1,
        "person_id": 1,
        "url": "https://example.com/photos/1",
        "created_at": "2020-12-05T06:33:19.847425+00:00",
        "updated_at": "2020-12-05T06:33:19.847425+00:00"
      },

      〜 略 〜

      {
        "id": 5,
        "person_id": 2,
        "url": "https://example.com/photos/4",
        "created_at": "2020-12-05T06:33:43.201778+00:00",
        "updated_at": "2020-12-05T06:33:43.201778+00:00"
      }
    ]
  }
}

この様にGraphQLでは、ただ取得したいテーブル、カラムを追加するだけで取得できます。
 

リレーションを使用したQuery

personsphotos をそれぞれ別で取得を行いました。
ですが実際にはリレーションに紐づく値のみを取得したいというケースの方が多いかと思います。

次は person_id が既知の場合に、それに紐づく photo を取得する例です。

Rails

person = Person.find(1)

photo_urls person&.photos&.each_with_object([) do |photo, hash|
  hash << {url: photo.url}
end

data = {
  name: person.name,
  birthday: person.birthday,
  photos: photo_urls
}

GraphQL

{
  persons_by_pk(id: 1) {
    photos{
      url
    }
    name
    birthday
  }
}

出力結果

{
  "data": {
    "persons_by_pk": {
      "photos": [
        {
          "url": "https://example.com/photos/1"
        },
        {
          "url": "https://example.com/photos/2"
        },
        {
          "url": "https://example.com/photos/3"
        },
        {
          "url": "https://example.com/photos/3"
        }
      ],
      "name": "佐藤太郎",
      "birthday": "2000-01-01"
    }
  }
}

この様にリレーションを貼っていれば、通常のカラムを取得するように取得することができます。
この辺りからGraphQLの方がnull回避やループ処理等を記述しなくて良くなるので、単純なります。
(実際にはRailsの場合はURLを詰め直す作業は行わないと思いますが)

件数を算出するQuery

開発する上で、合計値、最大値、平均値等の値を算出したくなる時があると思います。
今回はIDが1の person が投稿した写真の件数を取得してみましょう

Rails

photos = Person.find(1).photos

data = photos.each_with_object({}) do |photo, hash|
  hash << {
    id: photo.id,
    url: photo.url,
    photo_person_tag: {
      count: photo.photo_person_tags.count
    }
  }
end

aggregate の様なGraphQL側でしか使用しない名前は省いています。

GraphQL

query {
  persons_by_pk(id: 1){
    photos {
      url
      id
      photo_person_tags_aggregate {
        aggregate {
          count(columns: id)
        }
      }
    }
  }
}

出力結果

{
  "data": {
    "persons_by_pk": {
      "photos": [
        {
          "url": "https://example.com/photos/1",
          "id": 1,
          "photo_person_tags_aggregate": {
            "aggregate": {
              "count": 1
            }
          }
        },

          

        {
          "url": "https://example.com/photos/3",
          "id": 4,
          "photo_person_tags_aggregate": {
            "aggregate": {
              "count": 0
            }
          }
        }
      ]
    }
  }
}

この様にGraphQLでも単純な演算処理は備わっています。
ただ、単純な演算自体はRailsが圧倒的に記述しやすいですね。

フラグメントを使用したQuery

フラグメントは同じクエリを複数の場所で使い回すことができる選択セットです。
Railsで言う所の、処理をメソッドに切り出す事ですね。
それでは一つ前の例を元に見ていきましょう。

Rails

今回はRails側は既に count メソッドとして切り出されていたものを使用したので割愛します。

GraphQL

fragment personInThePhotoCount on photos {
      photo_person_tags_aggregate {
        aggregate {
          count(columns: id)
        }
      }
}

query {
  persons_by_pk(id: 1){
    photos {
      url
      id
      ...personInThePhotoCount
    }
  }
}

出力結果

{
  "data": {
    "persons_by_pk": {
      "photos": [
        {
          "url": "https://example.com/photos/1",
          "id": 1,
          "photo_person_tags_aggregate": {
            "aggregate": {
              "count": 1
            }
          }
        },

          

        {
          "url": "https://example.com/photos/3",
          "id": 4,
          "photo_person_tags_aggregate": {
            "aggregate": {
              "count": 0
            }
          }
        }
      ]
    }
  }
}

変数を使用したQuery

実際にアプリケーションとして運用する際にClientを操作するユーザによって入力されたものをWhere句などでクエリに使用する事があります。
その中で意識する事として入力値のバリデーションがあると思います。
そんな時に使用するものとしてクエリ変数 (Query Variables)があります。
クエリ中で使用される動的な変数を別で定義することにより、
型のチェックやnull回避等はもちろん、再利用性も高くなります。

次の例は名前が 佐藤 から始まり、 2005-01-01 より前に誕生日の人を取得するクエリです。

GraphQL

Query

query MyQuery($name: String!, $birthday: date) {
  persons(where: {name: {_like: $name}, birthday: {_lt: $birthday}}) {
    id
    name
    birthday
  }
}

Query Variables

{
    "name": "佐藤%", 
    "birthday": "2005-01-01"
}

出力結果

{
  "data": {
    "persons": [
      {
        "id": 1,
        "name": "佐藤太郎",
        "birthday": "2000-01-01"
      }
    ]
  }
}

query MyQuery($name: String!, $birthday: date)
このクエリの引数は2つで、
名前は String 型で入力必須
誕生日は date 型でnull許可されている例です。

Mutation

次はMutaionを説明します。Mutationは変更を加えたい時に使用します。
種類としては大きく分けて作成、更新、削除の3種類あります。

それではそれぞれ見ていきましょう。

レコード作成のMutation

Rails

Person.create(
    name: "佐藤三郎", 
    birthday: "2005-01-01"
)

GraphQL

Query

mutation MyMutation($name: String!, $birthday: date!) {
  insert_persons_one(object: {birthday: $birthday, name: $name}) {
    id
    name
    birthday
    created_at
    updated_at
  }
}

Query Variables

{
    "name": "佐藤三郎", 
    "birthday": "2005-01-01"
}

出力結果

{
  "data": {
    "insert_persons_one": {
      "id": 6,
      "name": "佐藤三郎",
      "birthday": "2005-01-01",
      "created_at": "2020-12-08T10:27:17.430325+00:00",
      "updated_at": "2020-12-08T10:27:17.430325+00:00"
    }
  }
}

この様にQueryとMutationは基本同一の形になります。
また、insert_persons_oneの {} の部分は返却値になります。必要なものだけを入力してください。
insert_persons_one(key: value) { 【ここの部分】 }

また、 insert_persons_one という名前からわかるように、これは単数のレコード作成になるので、
複数の場合は insert_persons で作成できます。

値変更のMutation

今回は値を変更しようと思います。
person のIDが5のユーザの誕生日を 2002-01-01 に変更します。

Rails

person = Person.find(5)
person.birthday = "2002-01-01"
person.save

GraphQL

Query

mutation MyMutation($birthday: date) {
  update_persons(where: {id: {_eq: 5}}, _set: {birthday: $birthday}) {
    affected_rows
  }
}

Query Variables

{
    "birthday": "2002-01-01"
}

出力結果

{
  "data": {
    "update_persons": {
      "affected_rows": 1
    }
  }
}

更新対象の特定は、今回はIDを用いましたが普通に他のものを使用して問題ありません。
また、更新内容は _set 内に更新情報を入力することで更新できます。

今回の返却値は "affected_rows": 1 となっていますが、これは返却値を更新した行数を設定したため
1行だけ変更があった事が確認できます。

削除を行うMutation

今回はIDが5の person を削除してみたいと思います。

Rails

person = Person.find(5)
person.destroy

GraphQL

mutation {
  delete_persons(where: {id: {_eq: 5}}) {
    affected_rows
  }
}

出力結果

{
  "data": {
    "delete_persons": {
      "affected_rows": 1
    }
  }
}

こちらも同様に簡単に削除を行う事ができました。

まとめ

最後まで読んでくださってありがとうございます。
GraphQLは表現には制限がありますし、スキーマ情報を外に公開しているので、
スキーマの変更に対して脆くなってしまう問題こそあれ、Rails以上に簡単にRESTFul APIを作成することができます。

今回記述させて頂いた内容はまだGraphQLの一部でしかありません。
より一層GraphQLを知りたい、使いたいという方はまずはGraphQLの公式ドキュメントを見てみると良いでしょう。
https://graphql.org/learn/

ただし、今回使用したGraphQLエンジンのHasuraはGraphQLとは大きく異なっている部分があるので、 HasuraのGraphQLを使用したい場合はHasuraのGraphQLのドキュメントを読んでみてください。

https://hasura.io/docs/1.0/graphql/core/index.html

最後に

弊社ではGraphQLを仮説検証段階の、速度感をもったサービス開発で少しづつ取り入れ始めています。

そんな技術もサービスも一緒に検証したいエンジニアを募集しています。

カジュアルな面談もありますので、一度下記のURLから確認してみてください!
https://corp.toreta.in/recruit/midcareer/

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

RailsエンジニアがGraphQLを触って比較してみた

はじめに

こんにちは!榊原です。
トレタに入社して1年が経ちました。
この1年はとても変化が多くあった年だったと感じています。

特に今年の1月から会社全体でフルリモート化や、
DXを意識した飲食店での自分のスマホでオーダーできるシステムや予約時に座席を指定して予約できるシステムなど、
時代の変化に対応した様々な新規サービスを発表した年でもあります。

発表したサービスの中にはまだ市場に受け入れられるか仮設段階のものも数多くありました。
エンジニアの人数も限りがある中で、最速かつ柔軟な変化を求められる開発を進めるに当たって、
最近弊社で使われ始めているのがGraphQLです。

今回はそんなGraphQLを弊社でも創業当初から運用されてきたRuby on Railsとの比較を中心とした説明とさせていただきます。

GraphQLを説明をするに当たって、グラフ理論は欠かせないのですがそちらは他の記事が多数存在するため今回は割愛させていただきます。

対象となる読者

  • Railsは使用していてもGraphQLは触ったことがないけど触ってみたい
  • サーバーサイドを担当していたけど、業務で使用しなければならなくなった

対象アプリケーション

GraphQLって何者なの?

GraphQLはFacebookによって開発されました。
元々モバイルアプリはWeb用のラッパーアプリでした。
しかしRESTfulAPIサーバーとFQL(Facebook用のSQL)のデータテーブルで運用されてましたが、
パフォーマンスの低下や度々クラッシュするなど改善が求められました。
そこでFacebookのクライアント、サーバーアプリケーションの性能上のかだいと、データ構造の要件を満たす解決策として誕生したのがGraphQLです。
そして現在ではFacebookのほぼ全てのデータ取得にGraphQLが用いられています。

GraphQLのクライアントアプリケーションとしては、 Relay, Apollo, そして最近ではHasura等があります。

初めの一歩 (GraphQLでできること)

シンプルなGraphQLの記述例ですが、
例えばRailsでPersonsテーブルの情報全て取得するAPIがあるとします

Rails

def PersonsController
  def index
    data = Person.all
    render json: data
  end
end

この場合GraphQLでは下記の様なクエリを記述するだけで取得できます

GraphQL

query {
  persons {
    id
    name
    birthday
    created_at
    updated_at
  }
}

出力結果はGraphQLでは data のハッシュ内に梱包される形になりますが、下記の様な形式で出力されます。

{
  "data": {
    "persons": [
      {
        "id": 1,
        "name": "佐藤太郎",
        "birthday": "2000-01-01",
        "created_at": "2020-12-05T06:31:43.250885+00:00",
        "updated_at": "2020-12-05T06:31:43.250885+00:00"
      },

      〜 略 〜

      {
        "id": 4,
        "name": "伊藤花子",
        "birthday": "2010-03-03",
        "created_at": "2020-12-05T06:32:37.952758+00:00",
        "updated_at": "2020-12-05T06:32:37.952758+00:00"
      }
    ]
  }
}

この様にGraphQLはクエリ言語なため、RailsではあったModelやController、Render等を省き、
簡単なコードだけででRESTFullなAPIを作成することができます。

この後のRailsのサンプルコードとしては、Controllerや render は極力記述せず、ActiveRecordを用いたデータを中心とした記述とします。

使用するSchemaの説明

schema.png

今回使用するのは下記の3つのテーブルで、単純なSNSを想定して作成しました。
- persons: ユーザー
- photos: ユーザーが投稿した写真
- photo_person_tags: 写真にタグ付けされた人

シンプルなQuery

それでは先ほどのクエリを見ていきましょう。

query {       // Select句
  persons {   // テーブル名
    id        // カラム名 (以下同一)
    name
    birthday
    created_at
    updated_at
  }
}

GraphQLではCQRSを採用しているため、最初にQuery(Select句)は query を、Command(Create、Update、Delete句)は mutation を記述します。
今回はSelectなので query を使用しました。

次に、取得したいテーブル名とカラム名を記述すれば、先程の様な値が取得できます。

条件のあるQuery

全てを取得することはできても、条件に合ったもののみ取得したいケースは勿論あるでしょう。
その場合は下記の様に記述します。
名前に佐藤という文字が入っている人を検索します。

Rails

persons.where('name like ?','佐藤%').limit(2)

GraphQL

query {
  persons(where: {name: {_like: "佐藤%"}}, limit: 2) {
    id
    name
    birthday
    created_at
    updated_at
  }
}

persons の中にwhere句、Limit句が追加されただけですね。
基本的な表現は同じです。
条件式に使用できるものとしては下記で確認してください。

https://hasura.io/docs/1.0/graphql/core/queries/query-filters.html

複数のテーブルに対するQuery

実際にサービスを作成していると複数のテーブル情報を一度に取得したいというケースは多くの場面で遭遇すると思います。

ここでは、シンプルに personsphotos のデータを1つのJSON形式で出力してみます。

Rails

persons = persons.all
photos = photos.all

data = {
  persons: persons, 
  photos: photos
}

GraphQL

query {
  persons {
    id
    name
    birthday
    created_at
    updated_at
  }
  photos {
    id
    person_id
    url
    created_at
    updated_at
  }
}

出力結果

{
  "data": {
    "persons": [
      {
        "id": 1,
        "name": "佐藤太郎",
        "birthday": "2000-01-01",
        "created_at": "2020-12-05T06:31:43.250885+00:00",
        "updated_at": "2020-12-05T06:31:43.250885+00:00"
      },

      〜 略 〜

      {
        "id": 3,
        "name": "佐藤花子",
        "birthday": "2010-03-03",
        "created_at": "2020-12-05T06:32:37.249905+00:00",
        "updated_at": "2020-12-05T08:21:50.992633+00:00"
      }
    ],
    "photos": [
      {
        "id": 1,
        "person_id": 1,
        "url": "https://example.com/photos/1",
        "created_at": "2020-12-05T06:33:19.847425+00:00",
        "updated_at": "2020-12-05T06:33:19.847425+00:00"
      },

      〜 略 〜

      {
        "id": 5,
        "person_id": 2,
        "url": "https://example.com/photos/4",
        "created_at": "2020-12-05T06:33:43.201778+00:00",
        "updated_at": "2020-12-05T06:33:43.201778+00:00"
      }
    ]
  }
}

この様にGraphQLでは、ただ取得したいテーブル、カラムを追加するだけで取得できます。
 

リレーションを使用したQuery

personsphotos をそれぞれ別で取得を行いました。
ですが実際にはリレーションに紐づく値のみを取得したいというケースの方が多いかと思います。

次は person_id が既知の場合に、それに紐づく photo を取得する例です。

Rails

person = Person.find(1)

photo_urls person&.photos&.each_with_object([) do |photo, hash|
  hash << {url: photo.url}
end

data = {
  name: person.name,
  birthday: person.birthday,
  photos: photo_urls
}

GraphQL

{
  persons_by_pk(id: 1) {
    photos{
      url
    }
    name
    birthday
  }
}

出力結果

{
  "data": {
    "persons_by_pk": {
      "photos": [
        {
          "url": "https://example.com/photos/1"
        },
        {
          "url": "https://example.com/photos/2"
        },
        {
          "url": "https://example.com/photos/3"
        },
        {
          "url": "https://example.com/photos/3"
        }
      ],
      "name": "佐藤太郎",
      "birthday": "2000-01-01"
    }
  }
}

この様にリレーションを貼っていれば、通常のカラムを取得するように取得することができます。
この辺りからGraphQLの方がnull回避やループ処理等を記述しなくて良くなるので、単純なります。
(実際にはRailsの場合はURLを詰め直す作業は行わないと思いますが)

件数を算出するQuery

開発する上で、合計値、最大値、平均値等の値を算出したくなる時があると思います。
今回はIDが1の person が投稿した写真の件数を取得してみましょう

Rails

photos = Person.find(1).photos

data = photos.each_with_object({}) do |photo, hash|
  hash << {
    id: photo.id,
    url: photo.url,
    photo_person_tag: {
      count: photo.photo_person_tags.count
    }
  }
end

aggregate の様なGraphQL側でしか使用しない名前は省いています。

GraphQL

query {
  persons_by_pk(id: 1){
    photos {
      url
      id
      photo_person_tags_aggregate {
        aggregate {
          count(columns: id)
        }
      }
    }
  }
}

出力結果

{
  "data": {
    "persons_by_pk": {
      "photos": [
        {
          "url": "https://example.com/photos/1",
          "id": 1,
          "photo_person_tags_aggregate": {
            "aggregate": {
              "count": 1
            }
          }
        },

          

        {
          "url": "https://example.com/photos/3",
          "id": 4,
          "photo_person_tags_aggregate": {
            "aggregate": {
              "count": 0
            }
          }
        }
      ]
    }
  }
}

この様にGraphQLでも単純な演算処理は備わっています。
ただ、単純な演算自体はRailsが圧倒的に記述しやすいですね。

フラグメントを使用したQuery

フラグメントは同じクエリを複数の場所で使い回すことができる選択セットです。
Railsで言う所の、処理をメソッドに切り出す事ですね。
それでは一つ前の例を元に見ていきましょう。

Rails

今回はRails側は既に count メソッドとして切り出されていたものを使用したので割愛します。

GraphQL

fragment personInThePhotoCount on photos {
      photo_person_tags_aggregate {
        aggregate {
          count(columns: id)
        }
      }
}

query {
  persons_by_pk(id: 1){
    photos {
      url
      id
      ...personInThePhotoCount
    }
  }
}

出力結果

{
  "data": {
    "persons_by_pk": {
      "photos": [
        {
          "url": "https://example.com/photos/1",
          "id": 1,
          "photo_person_tags_aggregate": {
            "aggregate": {
              "count": 1
            }
          }
        },

          

        {
          "url": "https://example.com/photos/3",
          "id": 4,
          "photo_person_tags_aggregate": {
            "aggregate": {
              "count": 0
            }
          }
        }
      ]
    }
  }
}

変数を使用したQuery

実際にアプリケーションとして運用する際にClientを操作するユーザによって入力されたものをWhere句などでクエリに使用する事があります。
その中で意識する事として入力値のバリデーションがあると思います。
そんな時に使用するものとしてクエリ変数 (Query Variables)があります。
クエリ中で使用される動的な変数を別で定義することにより、
型のチェックやnull回避等はもちろん、再利用性も高くなります。

次の例は名前が 佐藤 から始まり、 2005-01-01 より前に誕生日の人を取得するクエリです。

GraphQL

Query

query MyQuery($name: String!, $birthday: date) {
  persons(where: {name: {_like: $name}, birthday: {_lt: $birthday}}) {
    id
    name
    birthday
  }
}

Query Variables

{
    "name": "佐藤%", 
    "birthday": "2005-01-01"
}

出力結果

{
  "data": {
    "persons": [
      {
        "id": 1,
        "name": "佐藤太郎",
        "birthday": "2000-01-01"
      }
    ]
  }
}

query MyQuery($name: String!, $birthday: date)
このクエリの引数は2つで、
名前は String 型で入力必須
誕生日は date 型でnull許可されている例です。

Mutation

次はMutaionを説明します。Mutationは変更を加えたい時に使用します。
種類としては大きく分けて作成、更新、削除の3種類あります。

それではそれぞれ見ていきましょう。

レコード作成のMutation

Rails

Person.create(
    name: "佐藤三郎", 
    birthday: "2005-01-01"
)

GraphQL

Query

mutation MyMutation($name: String!, $birthday: date!) {
  insert_persons_one(object: {birthday: $birthday, name: $name}) {
    id
    name
    birthday
    created_at
    updated_at
  }
}

Query Variables

{
    "name": "佐藤三郎", 
    "birthday": "2005-01-01"
}

出力結果

{
  "data": {
    "insert_persons_one": {
      "id": 6,
      "name": "佐藤三郎",
      "birthday": "2005-01-01",
      "created_at": "2020-12-08T10:27:17.430325+00:00",
      "updated_at": "2020-12-08T10:27:17.430325+00:00"
    }
  }
}

この様にQueryとMutationは基本同一の形になります。
また、insert_persons_oneの {} の部分は返却値になります。必要なものだけを入力してください。
insert_persons_one(key: value) { 【ここの部分】 }

また、 insert_persons_one という名前からわかるように、これは単数のレコード作成になるので、
複数の場合は insert_persons で作成できます。

値変更のMutation

今回は値を変更しようと思います。
person のIDが5のユーザの誕生日を 2002-01-01 に変更します。

Rails

person = Person.find(5)
person.birthday = "2002-01-01"
person.save

GraphQL

Query

mutation MyMutation($birthday: date) {
  update_persons(where: {id: {_eq: 5}}, _set: {birthday: $birthday}) {
    affected_rows
  }
}

Query Variables

{
    "birthday": "2002-01-01"
}

出力結果

{
  "data": {
    "update_persons": {
      "affected_rows": 1
    }
  }
}

更新対象の特定は、今回はIDを用いましたが普通に他のものを使用して問題ありません。
また、更新内容は _set 内に更新情報を入力することで更新できます。

今回の返却値は "affected_rows": 1 となっていますが、これは返却値を更新した行数を設定したため
1行だけ変更があった事が確認できます。

削除を行うMutation

今回はIDが5の person を削除してみたいと思います。

Rails

person = Person.find(5)
person.destroy

GraphQL

mutation {
  delete_persons(where: {id: {_eq: 5}}) {
    affected_rows
  }
}

出力結果

{
  "data": {
    "delete_persons": {
      "affected_rows": 1
    }
  }
}

こちらも同様に簡単に削除を行う事ができました。

まとめ

最後まで読んでくださってありがとうございます。
GraphQLは表現には制限がありますし、スキーマ情報を外に公開しているので、
スキーマの変更に対して脆くなってしまう問題こそあれ、Rails以上に簡単にRESTFul APIを作成することができます。

今回記述させて頂いた内容はまだGraphQLの一部でしかありません。
より一層GraphQLを知りたい、使いたいという方はまずはGraphQLの公式ドキュメントを見てみると良いでしょう。
https://graphql.org/learn/

ただし、今回使用したGraphQLエンジンのHasuraはGraphQLとは大きく異なっている部分があるので、 HasuraのGraphQLを使用したい場合はHasuraのGraphQLのドキュメントを読んでみてください。

https://hasura.io/docs/1.0/graphql/core/index.html

最後に

弊社ではGraphQLを仮説検証段階の、速度感をもったサービス開発で少しづつ取り入れ始めています。

そんな技術もサービスも一緒に検証したいエンジニアを募集しています。

カジュアルな面談もありますので、一度下記のURLから確認してみてください!
https://corp.toreta.in/recruit/midcareer/

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

Docker開発環境でCapybara, FactoryBotを使ったテスト環境を構築追加する

概要

docker開発環境でRSpecテスト環境を構築するやり方の備忘録としてまとめました。
独学なので間違っている部分があると思いますが、その時はご指摘頂けると嬉しいです。

以下の記事を参考にしました。
https://qiita.com/at-946/items/e96eaf3f91a39d180eb3
https://qiita.com/na-tsune/items/91630257294aa0ea4fc8

1.docker-compose.ymlの編集

docker-compose.yml
version: '3'
services:
    db:
        image: mysql:5.7
        environment:
            MYSQL_USER: root
            MYSQL_ROOT_PASSWORD: password
        ports:
            - "3306:3306"
        volumes:
            - ./db/mysql/volumes:/var/lib/mysql
    web:
        build: .
        command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
        volumes:
            - .:/myapp
            - gem_data:/usr/local/bundle
        ports:
            - 3000:3000
        depends_on:
            - db 
            - chrome # ←追加
        tty: true
        stdin_open: true
    chrome:
        image: selenium/standalone-chrome:latest
        ports:
            - 4444:4444
volumes:
    gem_data:

web:配下のdepends_onに-chromeを追記。
services:配下にchrome:以下を追記。

2.RSpecの導入

Gemfileに必要なgemを追記

Gemfile
group :development, :test do
  # Call 'byebug' anywhere in the code to stop execution and get a debugger console
  gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
  gem 'pry-rails'
  gem 'pry-byebug'
  #rspec用のgem 2つ追記
  gem 'rspec-rails', '~> 4.0.1'
  gem 'factory_bot_rails', '~>4.11'
end

dockerのbuild, up, gemのインストール,Rspecのインストールをする。

$ docker-compose run web bundle install
$ docker-compose build
$ docker-compose up
$ docker-compose run web rails g rspec:install

次に、
RSpecのインストールによって作成されるファイルの中に、"rails_helper.rb"の設定を記述していきます。ついでにtestディレクトリは削除しておく。

/spec/rails_helper.rb
#↓追記
Capybara.register_driver :remote_chrome do |app|
  url = "http://chrome:4444/wd/hub"
  caps = ::Selenium::WebDriver::Remote::Capabilities.chrome(
    "goog:chromeOptions" => {
      "args" => [
        "no-sandbox",
        "headless",
        "disable-gpu",
        "window-size=1680,1050"
      ]
    }
  )
  Capybara::Selenium::Driver.new(app, browser: :remote, url: url, desired_capabilities: caps)
end
#↑追記

#↓追記
  config.before(:each, type: :system) do
    driven_by :rack_test
  end

  config.before(:each, type: :system, js: true) do
    driven_by :remote_chrome
    Capybara.server_host = IPSocket.getaddress(Socket.gethostname)
    Capybara.server_port = 4444
    Capybara.app_host = "http://#{Capybara.server_host}:#{Capybara.server_port}"
  end
  #↑追記

最後に.rspecを編集して、rails_helper.rbの設定を読み取るようにします。

.rspec
--require spec_helper #削除する
--require rails_helper #追記

あとはテスト記述、実行できるか確認する。
terminal
$ docker-compose run web rspec [rspecテストファイルのpath]

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

JavaScriptでのUncaught TypeError: Cannot read property 'addEventListener' of null エラー

Javascriptの中で指定ページ以外のページのconsoleを見ると

Uncaught TypeError: Cannot read property 'addEventListener' of null

というエラーが突然出た件。

あれ?指定ページでは出ないのに、、なぜ?となったので備忘録として。

今回のコード

window.addEventListener("load",function(){
const priceGet = document.getElementById("item-price");
priceGet.addEventListener("input", () => {

以下略

原因は他のページではidがitem-priceの要素が存在しないため
priceGetがnullになってしまう。
nullに対してaddEventListenerを使用したことでエラーが発生した。

対応策として

window.addEventListener("load",function(){
const priceGet = document.getElementById("item-price");
if (!priceGet){ return false;}
priceGet.addEventListener("input", () => {

以下略
if (!priceGet){ return false;}

を記載することで
priceGetがnullの場合にそれ以降のコードを読まないように実装できる。

もし何か間違い等あれば教えていただきたいです!

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

Sidekiqの管理画面で500エラーが発生

Sidekiqの管理画面(Sidekiq::Web)で500エラーが発生しており、無事に解決できたので、対応方法をまとめておきます。

開発環境

  • Rails: 5.2.3
  • Ruby: 2.6.3
  • rack: 2.2.3
  • Redis: 4.1.2
  • Sidekiq: 6.0.0

発生していたエラー

NoMethodError (undefined method `match' for #<Rack::Session::SessionId:xxxxxxxxxxxxxx>):

やったこと

試してみたこと

1.セッションを消してみる
2.rackをアップデート
3.bundle updateしてみる

解決した方法

4.redis-rackをアップデートしてみる
5.redis-actionpackをアップデートしてみる


1.セッションを消してみる

config/routes.rb
require 'sidekiq/web'
Sidekiq::Web.set :sessions, false # 追加

管理画面は表示されるが、セッションを消しているだけなので、
根本的な解決とはなっていない。

2.rackをアップデート

bundle update rack

Note: rack version regressed from 2.2.3 to 2.0.9

エラーが変わらず。

3.bundle updateしてみる

bundle update
正常に動いたのでgemに問題がありそう。
今回の修正範囲以外にも影響が大きいため、gem全てがアップデートされるbundle updateは避けて、
一旦もとに戻して原因を探る。


4.redis-rackをアップデートしてみる

bundle update redis-rack

管理画面は正常に表示されたが、テストが失敗してしまう。

NoMethodError:
  undefined method `private_id' for "xxxxxxxxxxxxxxx":String
  Did you mean?  private_methods

5.redis-actionpackをアップデートしてみる

上記のredis-rackのアップデート実行後、
こちらの記事
https://github.com/redis-store/redis-rack/pull/50#issuecomment-567649953
を参考に
bundle update redis-actionpack

これでサーバー再起動後、Sidekiqの管理画面の500エラーが解消し、テストも通った。

まとめ

redis-rackredis-actionpackのアップデートで解決しました。

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

Rails newが上手くいかない時の対処法

今日1時間ほどコマンドプロンプトでrails newをしても必要なファイルが作成されず、苦戦したのでその対処法を残しておきます。

誰かの参考になれば嬉しいです。

では、本題ですがコマンドプロンプトでrails newを実行するとこんな画面が表示されてばかりで、欲しいファイルが作成されませんでした。
gemfileでdeviseをインストール出来ないことからこの異変に気付いたのですが、最初はずっとgemfile側の問題だと思っていたのですが、全く改善できず。。。

そこで、最初に上手くいっていたアプリがあったのでそれを参考にしようと思ったのですが、まさか一番最初の時点でミスっているなんて思わずドツボにハマり、1時間ちょっと使いました(笑)

スクリーンショット (3).png

で、改めてこのコマンドプロンプトの表示を見てみるとgitのところで止まっていることに気づき、ググってみるとgitのコマンドがコマンドプロンプト上で使えるようになっていないからだと分かりました!!

だから、解決策としてはrails new アプリ名 -Gと後ろに-Gをつけてgitをスキップすれば解決することが判明し、無事解決することが出来ホッとしているところです。

gitをwindowsのコマンドプロンプトで使えるように設定しておけばよかった。。。

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

Railsについて初心者なりに調べてみた

ドキュメント

Ruby on Rails ガイド:体系的に Rails を学ぼう

Railsドキュメント

Railsとは

  • Rubyにより構築されたWEBアプリケーションフレームワーク
  • MVCアーキテクチャに基づいて構築されている
  • 他のフレームワークより少ないコードでアプリケーション開発ができるように考慮されている
  • それを実現するためにRailsには制約が多く存在し、慣れるまでは少し窮屈に感じることもあるかも?

Rubyの他フレームワーク

Sinatra

・軽量なフレームワークで簡潔に記述できる、最小限の労力でWebアプリケーションをすばやく作れる。
・MVCアーキテクチャに基づいた設計ではない。
小規模開発に向き
http://sinatrarb.com/

HANAMI

・バージョン1.0が2017年4月にリリースされた比較的新しいフレームワーク。
・メモリの消費を抑えるために提供されている100以上の安定したAPIを利用できる
・応答速度などで高いパフォーマンスを発揮
長期的なメンテナンスを考え作られている
https://hanamirb.org/

Ramaze

・Sinatraと同様にシンプルかつ軽量で柔軟性のあるフレームワーク
・Rubyの書き方をそのまま踏襲できるようになっている
http://ramaze.net/

他言語フレームワークとの比較

Web開発フレームワークのシェアと推移

Stack Overflow

image.png

djangoとlaravelがトレンド上昇している。

ruby on rails は2011年以降、下降している。

Ruby on rails のトレンド下降している要因

Twitterが、Ruby on RailsからJavaVMへ移行する理由

Twitterの膨大化したアクセスを、railsで構築されたシステムよりもJavaVMの方が速やかに処理できる。

→大規模システム開発で使われるケースが世界的に減っている。

なぜ一時期、一世を風靡したRuby on railsが、「railsはもう終わった」と言われるようになったのか?

その一部の背景を上記で説明しました。以下は具体的にかかれている記事

「Railsは終わった」と言われる理由 - Qiita

ただ日本ではスタートアップ中心に仕事がまだまだたくさんある。

例、Cookpad, Gunosy, 食べログ, Freee, Crowdworks

開発環境

  • ローカル
  • Virtual Box
  • Docker
  • Cloud9(AWS)

開発の流れ(ローカル環境)

基本的にMVCモデルの設計に沿って、ファイルを作成する。

image.png
↑railsチュートリアルから抜粋

参考文献

Ruby on Railsだけじゃない!Rubyフレームワーク6選

Ruby on Rails チュートリアル:プロダクト開発の0→1を学ぼう

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

【Rails】チャットの吹き出しの向きを変える方法

本記事は 駆け出しエンジニアの第一歩!AdventCalendar2020 7日目の記事です。

作りたいもの

下図のように、自分のメッセージと自分以外のメッセージでユーザーアイコンの位置や吹き出しの向きが変わるチャット機能を作ります。

image.png

前提条件

  • チャット機能自体はできている
  • bootstrap4がインストールされている

やることまとめ

  1. 自分用の吹き出しと相手用の吹き出しのCSSクラスを用意する
  2. Viewファイルにて、メッセージごとに自分or自分以外のメッセージかを判定する
  3. それぞれの判定ごとに適用するCSSクラスを変える

1.自分用の吹き出しと相手用の吹き出しのCSSクラスを用意する

吹き出しの枠をデザインするCSSクラスを作成します。
自分のメッセージの場合、吹き出しの尻尾が左側に来るように、
自分以外のメッセージの場合、吹き出しの尻尾が右側に来るようにします。

// 自分のメッセージの吹き出し
.says {
  float: left;
  position: relative;
  width: calc(100% - 56px);
  padding: 16px;
  background: #ffffff;
  border-radius: 15px;
  line-height: 1.5;
  word-break: break-all;
}
.says:after {
  content: "";
  display: inline-block;
  position: absolute;
  top: 3px;
  left: -19px;
  border: 8px solid transparent;
  border-right: 18px solid #ffffff;
  -webkit-transform: rotate(35deg);
  transform: rotate(35deg);
}

// 自分以外のメッセージの吹き出し
.other-user-says {
  float: right;
  position: relative;
  width: calc(100% - 56px);
  padding: 16px;
  background: #ffffff;
  border-radius: 15px;
  line-height: 1.5;
  word-break: break-all;
}
.other-user-says:after {
  content: "";
  display: inline-block;
  position: absolute;
  top: 3px;
  right: -19px;
  border: 8px solid transparent;
  border-right: 18px solid #ffffff;
  -webkit-transform: rotate(145deg);
  transform: rotate(145deg);
}

2.Viewファイルにて、メッセージごとに自分or自分以外のメッセージかを判定する

each文でmessageを全て読み込んだ後、メッセージごとに紐づいているユーザーが自分か自分以外かをif文で判定します。

この後、判定結果に応じて、適用する吹き出しのCSSクラスを変えていきます。

message.html.erb
<% messages.each do |m| %>
    <!-- 自分のメッセージの場合 -->
    <% if m.user == current_user %>

    <!-- 自分以外のメッセージの場合 -->
    <% else %>

    <% end %>
<% end %>

3. それぞれの判定ごとに適用するCSSクラスを変える

自分のメッセージの場合、自分用の吹き出しのCSSクラスを適用し、
自分以外のメッセージの場合、自分以外用の吹き出しのCSSクラスを適用するようにします。

message.html.erb
<% messages.each do |m| %>
  <!-- 自分のメッセージの場合 -->
  <% if m.user == current_user %>
    <tr class="row justify-content-center">
      <!-- アイコンを左側に表示する -->
      <td class="col-2">
        <%= link_to attachment_image_tag(m.user, :image, :fill, 80, 80, fallback: "noimage.png", size:'80x80', class:"profile-image align-top"), user_path(m.user) %>
      </td>
      <!-- メッセージを右側に表示する -->
      <td class="col-10">
        <%= m.user.display_name %> <br>
        <!-- 自分用の吹き出しCSSクラスを適用する -->
        <div class="says">
          <p><%= safe_join(m.content.split("\n"),tag(:br)) %></p>
          <span><%= l m.created_at %></span>
        </div>
      </td>
    </tr>

  <!-- 自分以外のメッセージの場合 -->
  <% else %>
    <tr class="row justify-content-center">
      <!-- メッセージを左側に表示する -->
      <td class="col-10">
        <div class="col-11 float-right">
          <%= m.user.display_name %> <br>
        </div>
        <!-- 自分以外用の吹き出しCSSクラスを適用する -->
        <div class="other-user-says">
          <p><%= safe_join(m.content.split("\n"),tag(:br)) %></p>
          <span><%= l m.created_at %></span>
        </div>
      </td>
      <!-- アイコンを右側に表示する -->
      <td class="col-2">
        <%= link_to attachment_image_tag(m.user, :image, :fill, 80, 80, fallback: "noimage.png", size:'80x80', class:"profile-image align-top"), user_path(m.user) %>
      </td>
    </tr>
  <% end %>
<% end %>

終わりに

今回は私個人のアプリで実際に実装したものを題材にしたので、人によってはビューの書き方は異なってくると思います!

ご参考になれば幸いです…!

参考サイト

RailsでDM(ダイレクトメッセージ)を送れるようにしよう

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

railsテスト駆動する理由

railsのテスト駆動に関する参考になった記事があるので共有
https://teratail.com/questions/121833?link=qa_related_pc_sidebar

テストをする理由は今のコードと自分の認識を一致させることだったということ。

user_test.rb
class UserTest < ActiveSupport::TestCass
  test "nameの長さは50文字以内である事" do
    # エラーを起こすために、わざと51文字を設定する
    @user.name = "a" * 51

    # もし、正しくバリデーションが設定されていたら、
    # バリデーションがfalseになるはず
    # assert_notは引数の結果がfalseに時にテストが成功する
    assert_not @user.valid?
  end

  test "emailの長さは255文字以内である事" do
    # エラーを起こすために、わざと255文字を超える文字列を設定
    # "a" * 256だけでもいいでしょう
    @user.email = "a" * 244 + "@example.com"

    assert_not @user.valid?
  end

end

assert_notはassert_notは引数の結果がfalseの時にテストが成功とする命令
つまり現時点でバリテーションがかかれていないため、@user.validがtrueとなるため、テストは失敗。
今のコードと自分の認識はあっていたということになる。
じゃあバリテーションを書こう。
こうなるわけなのか。。。なるほど。
テストって意味あるの?って疑問だったが、効率良く開発を進めていく上で必要不可欠なんだと理解した。

(1) 仕様を先に決める(頭の中だけでもいいし、紙に書いたりしてもいい)
(2) 仕様をテストとして記述する
(3) 仕様を満たすために実装する

このステップが大事なんですね。
めちゃめちゃ参考になりました。テストの書き方とかも勉強しないとな

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

Ruby , Ruby on Railsの根っこを理解するための基礎知識② 〜クラスとインスタンス〜

【クラスとインスタンス】

クラスとは

共通で定義される部分をまとめる大元の型みたいなもの

インスタンスとは

クラスの型を使って作られた派生した型みたいなもの
※“”で囲うと文字列を入力ができるメソッドや、[]で囲う事でArrayメソッドが利用ができるのは、予めStringクラスやArrayクラスという用意されたクラスがあるからであり、クラス自体を自分で定義する事も可能

<クラスを自分で定義する方法>

class クラス名
  処理
end

※クラス名の頭文字は必ず大文字

<インスタンスを作成する方法>

変数名 = クラス名.new
※「.new」で、変数に代入

<クラスメソッドを定義する方法>

class クラス名
  def self.メソッド名
  end
end

※classメソッドは定義するメソッド名の前にself.をつける
※使い方は、クラス名.メソッド名

<クラスメソッドの使用例>

class Animal
  def self.greet
    p "こんにちは!Animalです!"
  end
end

とクラスとクラスメソッドを定義すると、「Animal.greet」と書く事で、「"こんにちは!Animalです!”」が出力される
※クラスメソッドは、クラスしか呼び出せないため、「animal = Animal.new」とインスタンスを作成して「animl.greet」では「"こんにちは!Animalです!”」を書き出す事は出来ない

<インスタンスメソッドを定義する方法>

class クラス名
  def メソッド名
  end
end

※クラスメソッドと違い「self.」をつけないだけ
※使い方は、インスタンス名.メソッド名

<インスタンスメソッドの使用例>

class Animal
  def greet
    p "こんにちは!Animalのインスタンスです!"
  end
end

と定義する事で、「animal = Animal.new」とインスタンスを呼び出せば、「animal.greet」と書く事で、「"こんにちは!Animalのインスタンスです!”」と書き出す事が出来る

【initialize】

コンストラクタと呼ばれ、インスタンスが作成された(newメソッドが実行された)タイミングで呼ばれるメソッドのこと
※クラスからインスタンスを作成するときに共通の処理を行いたい場面で使用

<使用例>

class Animal
  def initialize
    p "インスタンスが作られました"
  end
end

と定義しておく事で「animal = Animal.new」とインスタンスが作成された時点で「"インスタンスが作られました”」と書き出される

【クラス変数とインスタンス変数】

クラス内に定義する変数のこと

クラス変数とは

クラスで使用できる変数のことで、クラスと、そのクラスからできたインスタンスから、呼び出すことができる変数

<クラス変数の定義と代入>

class クラス名
  @@クラス変数名 = 代入したいデータ
end

インスタンス変数とは

インスタンスごとに独立し、インスタンスからのみアクセスすることができ、クラスからアクセスすることはできない変数

<インスタンス変数の定義と代入>

class クラス名

  def インスタンス変数名=(代入したいデータ名)
    @インスタンス変数名 = 代入したいデータ名
  end

  def インスタンス変数名
    @インスタンス変数名
  end

end

と定義し、「インスタンス名 = クラス名.new」でインスタンスを作成後、「インスタンス名.インスタンス変数名 = 代入したいデータ」で代入が可能
※インスタンス変数はクラス内からしか呼び出せないため、上記の通りセッターとゲッターと呼ばれるメソッドをそれぞれ定義している

セッターとは

インスタンス変数の値をセットするためのメソッドで「def インスタンス変数名=」と定義するのが一般的

ゲッターとは

インスタンス変数の値を取得するためのメソッドで「def インスタンス変数名」と定義するのが一般的
※流れ的には、インスタンスを作成し、インスタンス変数にデータを代入し、代入された変数をゲッターでクラスが取得し、セッターでセットし、インスタンス名.インスタンス変数名で呼び出されている

<attr_accessor>

上記のゲッターとセッターは、この「attr_accessor」を使用する事で、一括で指定することもできる

class クラス名
  attr_accessor :インスタンス変数名
end

と定義し、「インスタンス名 = クラス名.new」でインスタンス作成後、「インスタンス名.インスタンス変数名 = 代入したいデータ」

<クラス変数の使用例>

class Animal
  @@counter = 0

  def initialize
    @@counter += 1
  end

  def self.get_counter
    return @@counter
  end

end

と定義する事で、

Animal.new
p Animal.get_counter

と書かれた際に、カウントされた数字が出力される
※「+= 1」は、クラス変数に1加算すると言う定義
※「return」は、メソッドの途中で抜け出し、その行の戻り値を返したい時に定義するメソッドで、省略も可能だが、複数行ある場合は、最後の戻り値が返される

<インスタンス変数の使用例>

class Animal

  def name=(value)
    @name = value
  end

  def name
    @name
  end

end

と定義する事で、「animal = Animal.new」とインスタンスを作成した後、「animal.name = “サル”」と代入する事で、「p animal.name」と書けば「”サル”」と書き出される

<インスタンス変数で、attr_accessorを使用した場合>

class Animal
  attr_accessor :name
end

と定義する事で、「animal = Animal.new」とインスタンスを作成した後、「animal.name = “サル”」と代入する事で、「p animal.name」と書けば「”サル”」と書き出される

【継承】

既存のクラスを元に新しいクラスを作成すること

<定義の仕方>

class クラス名 < 継承したいクラス名

<使用例>

class Animal
  def self.greet
    p "こんにちは!Animalです!"
  end
end

と定義し、

class Dog < Animal
end

と継承し「Dog.greet」を書けば、「"こんにちは!Animalです!”」と書き出される
※継承後にクラス独自のメソッドも同じように定義可能

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

rails マイグレーションファイルあれこれ

rails generate model User name:string email:string

Userのモデルをnameはstring型で、emailはstring型で作成という意味。
今までrails g model User のみをターミナルで入力してマイグレーションファイルをいじっていたので、このやり方は時短になるなあとおもった

以下マイグレーションファイル

XXXXXXXXXXXXX_create_users.rb
class CreateUsers < ActiveRecord::Migration[6.0]
  def change
    create_table :users do |t|
      t.string :name
      t.string :email

      t.timestamps
    end
  end
end

changeメゾットはcreate_tableというrailsのメゾットを呼び出し
create_tableメソッドはブロック変数を1つ持つブロックを受け取り,ここでは(“table”の頭文字を取って)t
ブロックの最後の行t.timestampsは特別なコマンドで
created_atとupdated_atという2つの「マジックカラム(Magic Columns)」を作成

$ rails db:migrate

でマイグレーションファイルが実行される。
実行されたマイグレーションファイルはいじっても更新されない
マイグレーションファイルが実行される前に戻りたいときは
$ rails db:rollback
このコマンドで戻すことができる

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

コンソールでDelayed::Jobを実行する手順

ローカル開発中にたまに使うときに忘れがちなので備忘録として。

以下でキューに入ったjobを実行可能

Delayed::Job.find(x).invoke_job

実行した後はレコード削除

Delayed::Job.find(x).destroy

以上、

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

【Rails Chart グラフ】Railsで、投稿に連動した美しいグラフ(レーダーチャート)を作ってみよう

【Rails Chart グラフ】Railsで、投稿に連動した美しいグラフを作ってみよう

Railsで、例えば
・投稿した成績をグラフ化したい時
・勉強時間をグラフ化したい時
など、様々な場面で、グラフを生成したいことがあるかと思います!

そこで今回は、chart.jsを使って、グラフを生成し、投稿の情報と連動させてみたいと思います!
image.png

ちなみに、カラムは
Postモデルの
・title(投稿のタイトル)
・rate(グラフの観点1個目)
・kindness(グラフの観点2個目)
・sadness(グラフの観点3個目)
・bitterness(グラフの観点4個目)
を用意し、それぞれの観点からの点数をグラフ化したレーダーチャートを実装します

実装までのステップ

1:chart.jsをCDN経由でRailsアプリに導入
2:グラフ生成のコード記述
3:ちょこっとコードいじって投稿と連動させる

それでは張り切っていきましょう!

1:chart.jsをCDN経由でRailsアプリに導入

こちらからCDNを作成しましょう!
スクリーンショット 2020-12-07 14.49.44.png

スクリーンショット 2020-12-07 14.52.25.png
そして、view > layouts > application.html.erbの

タグにscriptを記述しましょう!
application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title>StarFunction</title>
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>

    <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
    <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
    <script src="ここに先ほどコピペしたものを貼り付けでください!"></script>
  </head>

2:グラフ生成のコード記述

グラフを出したいViewに記述してください!

index.html.erb
<canvas id="myChart"></canvas>
<script>
var ctx = document.getElementById("myChart");
var myChart = new Chart(ctx, {
  type: 'radar',
  data: {
    labels: ["Rate", "Kindness", "Sadness", "Bitterness"],
    datasets: 
    [
        {
        label: '自己分析第1回',
        backgroundColor: 'rgba(102,255,129,0.2)',
        borderColor: 'rgba(122,255,129,0.2)',
        data: [40, 42, 42, 43]
        },
        {
        label: '自己分析第2回',
        backgroundColor: 'rgba(122,205,129,0.2)',
        borderColor: 'rgba(122,255,129,0.2)',
        data: [40, 42, 42, 43]
        },
        {
        label: '自己分析第3回',
        backgroundColor: 'rgba(122,255,109,0.2)',
        borderColor: 'rgba(122,255,129,0.2)',
        data: [40, 42, 42, 43]
        }
    ]
  },
  options: {
      scale: {
          ticks: {
              suggestedMin: 0,
              suggestedMax: 100
          }
      }
  }
});
</script>

こんな感じになりましたかね!?
image.png

3:ちょこっとコードいじって投稿と連動させる

index.html.erb
<canvas id="myChart"></canvas>
<script>
var ctx = document.getElementById("myChart");
var myChart = new Chart(ctx, {
  type: 'radar',
  data: {
    labels: ["Rate", "Kindness", "Sadness", "Bitterness"],
    datasets: 
    [
      <% @posts.each do |post| %>
        {
        label: '<%= post.title %>',
        backgroundColor: 'rgba(122,255,129,0.2)',
        borderColor: 'rgba(122,255,129,0.2)',
        data: [<%= post.rate %>, <%= post.kindness %>, <%= post.sadness %>, <%= post.bitterness %>]
        },
      <% end %>
    ]
  },
  options: {
      scale: {
          ticks: {
              suggestedMin: 0,
              suggestedMax: 5
          }
      }
  }
});
</script>

こんな感じに投稿と連動します
スクリーンショット 2020-12-07 15.24.23.png

グラフの観点を増やしたい時

index.html.erb
 data: {
    labels: ["Rate", "Kindness", "Sadness", "Bitterness"],

ここに観点を追加し、

index.html.erb
 data: {
    data: [40, 42, 42, 43]

ここにもデータの値を追加してください。

データの数値の最小値、最大値を変えたい時

index.html.erb
  options: {
      scale: {
          ticks: {
              suggestedMin: 0,
              suggestedMax: 100
          }
      }
  }

ここの
suggestedMin: 0,
suggestedMax: 100
値を変えてあげてください!?

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

ローカルで開発したアプリケーションをS3、EC2、RDSを使ってとりあえず動かしてみる

はじめに

こちらの記事で作成したアプリをAWSのサービスを使ってとりあえず動くようにしてみる記事です。

おおまかな流れ

今回はAWSのS3、EC2、RDSのサービスを使ってとりあえずrailsサーバーを立ち上げてアプリケーションが閲覧できる状態に持っていきたいと思います。

  1. S3のバケット作成、EC2インスタンス立ち上げ、RDSデータベース作成
  2. EC2にアプリのソースをクローン、環境構築
  3. EC2上でrailsサーバー立ち上げて実際に見れることを確認

今回の作業用のIAMユーザー作成

AWSのルートユーザーしかもっていなかったので、今回の作業用にIAMユーザーを作りました。(IAMユーザー自体は作らなくてもルートユーザーの操作から作業を進めることはできますが、作業に必要な権限のみを持ったIAMユーザーを作るのがセオリーです。)
Screenshot from 2020-12-07 11-45-28.png
RDS、EC2、S3それぞれにフルアクセスできるグループを作成して、グループ内にユーザーを追加しました。

S3バケット作成

ローカルでpublic/images配下に置いていた全画像をS3バケットにアップロードします。
今回はS3上に置いた画像のパスを直接指定してアプリ上で表示させたかったので、アップロードの際のアクセスコントロールリストのアクセス許可をパブリックにしています。
Screenshot from 2020-12-07 11-58-04.png
これでS3上の画像パスを直接指定することで画像を表示できます。

public/images/配下を指定していたview側の画像パスもS3のパスを指定するように変更しておきます。
今回はS3アドレスドメインをヘルパ関数で定義しておいて、viewの画像パス指定箇所に関数を呼び出す形で対応しました。

# application_helper.rb
def aws_s3_path
  'https://rails-persona5.s3-ap-northeast-1.amazonaws.com'
end

EC2インスタンス起動

無料枠で作成できるt2.microでEC2インスタンスを作成しました。
Screenshot from 2020-12-07 12-37-41.png

EC2インスタンス作成は特に必要な詳細設定等はありませんでしたが、パブリックDNSを指定してブラウザで開けるようにしたかったので、インスタンスが属するセキュリティグループのインバウンドルールを編集する必要がありました。
Screenshot from 2020-12-07 13-02-54.png
デフォルトで設定されているSSHルールに加えて、railsサーバーを起動するポート3000に対して0.0.0.0/0からのアクセスを許可します。(0.0.0.0は任意のIPv4アドレス)
Screenshot from 2020-12-07 12-53-22.png
これでパブリックDNSのポート3000を指定してEC2にブラウザでアクセスできます。

RDSデータベースの作成

無料利用枠でRDSデータベースを作成しました。エンジンはローカル開発環境に合わせてmysqlを設定しています。
EC2インスタンスからRDSにアクセスできるようにするために、RDSデータベースが属しているセキュリティグループに関してもインバウンドルールを設定する必要がありました。

  • データベースが属するセキュリティグループのインバウンドルール編集画面 Screenshot from 2020-12-07 13-19-51.png

MYSQL/Auroraルールを追加して、ソースにはEC2が属するセキュリティグループ(今回はlaunch-wizard-2)を指定します。EC2が属するセキュリティグループに、データベースが属するセキュリティグループへのアクセスを許可することになります。
これでEC2からRDSにアクセスできます。

  • ターミナル上でEC2経由でRDSにアクセスできることを確認 Screenshot from 2020-12-07 13-33-00.png

EC2上でrailsサーバー起動

EC2インスタンスにソースをクローン、database.ymlにRDSデータベースのホスト名、ユーザー名、パスワードを設定して、マイグレーションを実行後、ローカルのデータベース上のデータをダンプしてRDSデータベースにリストアしてデータの準備をします。
またrailsサーバー起動時にBlocked hostエラーが出たので対応しました。

(実はこの他にもgemのバージョンやyarnのインストールなどいろいろつまづきました。このあたりの環境構築はdockerを利用すれば簡単になるらしいので勉強していきたいと思います。)

全ての準備が整ったら以下のコマンドでrailsサーバー起動です。

bundle exec rails s -b 0.0.0.0

railsサーバーを起動したらEC2のパブリックDNSの3000ポート指定してブラウザからアクセスしてみます。

Screenshot from 2020-12-07 14-00-21.png
トップ画面が表示できました!

画像についても問題なく表示できているようです。

Screenshot from 2020-12-07 14-09-16.png

さいごに

今回の作業はここまでです。AWSのサービスを実際に触ることで理解を深めることができました。特にセキュリティグループの考え方については作業前の理解がほぼほぼ0だったので勉強になりました。
EC2上で実際にrailsサーバーを立ち上げる時に環境構築でいろいろつまづいたので、dockerのコンテナを利用したソース管理を勉強していきたいですね。また今回作った環境はローカルの開発環境をAWSサービス上にコピーしただけなので、railsサーバー起動時にも開発(development)環境として立ち上げているのみです。実際にwebアプリケーションを運用していく際には本番(production)環境を構築しなければならないので、そちらの構築方法に関しても勉強していきたいです。

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

serializerを使って紐付けされたモデルの値を簡単に取る方法

概要

プログラミング初心者がRailsでAPIを作るときの発見を書いてみました
誰かの参考になれば幸いです

やりたいこと

comment_serializerでcommentモデルに紐づいているuserモデルの中のaccountnameだけをjsonで渡したい

model同様にhas_manyなどを使用して書く

comments_serializer.rb
class CommentSerializer < ActiveModel::Serializer
  attributes :id, :content

  has_many :user
end

これでも取ることができるがuser_serializerファイルの作成やattributesの記述だったり色々と面倒
もっと簡単に値を取得したい!!

objectを使用する

comments_serializer.rb
class CommentSerializer < ActiveModel::Serializer

  attributes :id, :content, :accountname

  def accountname
    object.user.accountname
  end

end

objectを使うことでuser_serializerファイル作成も行うことなく値を取ることができる

 
 

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

Ruby , Ruby on Railsの根っこを理解する為に最低限必要な基礎知識

【ディレクトリとファイルの作成・実行コマンド】

ディレクトリの作成

$ mkdir 作りたいディレクトリ名

ファイルの作成

$ touch 作りたいファイル名.rb

ファイルの実行

$ ruby 実行したいファイル名.rb

※「.rb」はRubyの拡張子

【基礎用語】

データ型

オブジェクトが文字列なのか数値なのかを区別する種類のこと
※例”1”は文字列のデータ型なので、.to_iメソッドで整数に変換し計算を行う

文字列

データ型の一種で、テキストデータ

数値

データ型の一種で、計算を行える数字データ

変数

文字列や数値、計算などを定義されたデータ
※「変数名 = 数値や計算など」で代入し、「p 変数名」などにて、呼び出したり、出力する

<使用例>

「date = 2020」と定義すれば「p = date」と書いた時「2020」と出力される
※「=」は等しいと言う意味ではなく、左の値に右の値を代入する(定義する)と言う意味となる

配列

同じ括りのオブジェクトを一つにまとめる事
「変数名 = [“A”,”B”,”C”]」などで定義し、「p 変数名[インデックス番号]」などで呼び出したり、出力する

<使用例>

「fruits = ["リンゴ", "メロン", "バナナ”]」と定義した時、「p fruits[1]」と書いて、「メロン」と出力される
※インデックス番号は0から始まる番号

ハッシュ

まとめられた値のデータ型につける事ができる名前の事
「変数名 = [データ1, データ2, データ3]」などと複数のデータがまとめられた時に何を意味するか分からなくなってしまわないよう「変数名 = { ハッシュ名1: データ1, ハッシュ名2: データ2, ハッシュ名3: データ3}」とラベルをつける事で、p 変数名[:ハッシュ名]と呼び出せるように出来る

<使用例>

「fruits = { name: 'りんご', price: 100 }」と定義する事で、「p fruits[:name]」と書けば「’りんご’」と返ってくる

【メソッド】

プログラムが実行された時に行われる処理の事
デフォルメされたものもあれば、自分で作る事もできる関数のようなもので、「.」に続けて記載する事で、呼び出し使う事ができる

<使用例>

「length」と言うデフォルメされている文字数を返すメソッドを使う場合、「p "りんご".length」と記載する事で、「3」が返ってくる
※メソッドを呼び出すオブジェクト(例で言う”りんご”)のことをレシーバと呼び、メソッドを実行して返ってくる結果を返り値と呼ぶ

<自分で定義する方法>

「def メソッド名」でメソッドを定義する宣言を行い、処理を書いた最後に「end」で括り、メソッド名を書いて呼び出し使用する事ができる

<自分で定義する時の使用例>

def apple
  p "りんご"
end

と定義した場合「apple」と書くと、「りんご」が返ってくる

<メソッドに引数を渡す方法>

「def メソッド名(引数名)」と宣言し、「p 引数名」と処理を書く事で、「メソッド名(引数名)」を記載した際に入れた引数名をメソッドが受け取り出力する

<メソッドに引数を渡す時の使用例>

def fruits(name)
  p name
end

と定義した場合「fruits(“りんご”)」と書けば“りんご”と、fruits("バナナ”)と書けば”バナナ”と、それぞれ返ってくる

【クラスとインスタンス】

クラスとは

共通で定義される部分をまとめる大元の型みたいなもの

インスタンスとは

クラスの型を使って作られた派生した型みたいなもの
※“”で囲うと文字列を入力ができるメソッドや、[]で囲う事でArrayメソッドが利用ができるのは、予めStringクラスやArrayクラスという用意されたクラスがあるからであり、クラス自体を自分で定義する事も可能

<クラスを自分で定義する方法>

class クラス名
  処理
end

※クラス名の頭文字は必ず大文字

<インスタンスを作成する方法>

変数名 = クラス名.new
※.newで、変数に代入

<クラスメソッドを定義する方法>

class クラス名
  def self.メソッド名
  end
end

※classメソッドは定義するメソッド名の前にself.をつける
※使い方は、クラス名.メソッド名

<クラスメソッドの使用例>

class Animal
  def self.greet
    p "こんにちは!Animalです!"
  end
end

とクラスとクラスメソッドを定義すると、「Animal.greet」と書く事で、「"こんにちは!Animalです!”」が出力される
※クラスメソッドは、クラスしか呼び出せないため、「animal = Animal.new」とインスタンスを作成して「animl.greet」では「"こんにちは!Animalです!”」を書き出す事は出来ない

<インスタンスメソッドを定義する方法>

class クラス名
  def メソッド名
  end
end

※クラスメソッドと違い「self.」をつけないだけ
※使い方は、インスタンス名.メソッド名

<インスタンスメソッドの使用例>

class Animal
  def greet
    p "こんにちは!Animalのインスタンスです!"
  end
end

と定義する事で、「animal = Animal.new」とインスタンスを呼び出せば、「animal.greet」と書く事で、「"こんにちは!Animalのインスタンスです!”」と書き出す事が出来る

【initialize】

コンストラクタと呼ばれ、インスタンスが作成された(newメソッドが実行された)タイミングで呼ばれるメソッドのこと
※クラスからインスタンスを作成するときに共通の処理を行いたい場面で使用

<使用例>

class Animal
  def initialize
    p "インスタンスが作られました"
  end
end

と定義しておく事で「animal = Animal.new」とインスタンスが作成された時点で「"インスタンスが作られました”」と書き出される

【クラス変数とインスタンス変数】

クラス内に定義する変数のこと

クラス変数とは

クラスで使用できる変数のことで、クラスと、そのクラスからできたインスタンスから、呼び出すことができる変数

<クラス変数の定義と代入>

class クラス名
  @@クラス変数名 = 代入したいデータ
end

インスタンス変数とは

インスタンスごとに独立し、インスタンスからのみアクセスすることができ、クラスからアクセスすることはできない変数

<インスタンス変数の定義と代入>

class クラス名

  def インスタンス変数名=(代入したいデータ名)
    @インスタンス変数名 = 代入したいデータ名
  end

  def インスタンス変数名
    @インスタンス変数名
  end

end

と定義し、「インスタンス名 = クラス名.new」でインスタンスを作成後、「インスタンス名.インスタンス変数名 = 代入したいデータ」で代入が可能
※インスタンス変数はクラス内からしか呼び出せないため、上記の通りセッターとゲッターと呼ばれるメソッドをそれぞれ定義している

セッターとは

インスタンス変数の値をセットするためのメソッドで「def インスタンス変数名=」と定義するのが一般的

ゲッターとは

インスタンス変数の値を取得するためのメソッドで「def インスタンス変数名」と定義するのが一般的
※流れ的には、インスタンスを作成し、インスタンス変数にデータを代入し、代入された変数をゲッターでクラスが取得し、セッターでセットし、インスタンス名.インスタンス変数名で呼び出されている

<attr_accessor>

上記のゲッターとセッターは、この「attr_accessor」を使用する事で、一括で指定することもできる

class クラス名
  attr_accessor :インスタンス変数名
end

と定義し、「インスタンス名 = クラス名.new」でインスタンス作成後、「インスタンス名.インスタンス変数名 = 代入したいデータ」

<クラス変数の使用例>

class Animal
  @@counter = 0

  def initialize
    @@counter += 1
  end

  def self.get_counter
    return @@counter
  end

end

と定義する事で、

Animal.new
p Animal.get_counter

と書かれた際に、カウントされた数字が出力される
※「+= 1」は、クラス変数に1加算すると言う定義
※「return」は、メソッドの途中で抜け出し、その行の戻り値を返したい時に定義するメソッドで、省略も可能だが、複数行ある場合は、最後の戻り値が返される

<インスタンス変数の使用例>

class Animal

  def name=(value)
    @name = value
  end

  def name
    @name
  end

end

と定義する事で、「animal = Animal.new」とインスタンスを作成した後、「animal.name = “サル”」と代入する事で、「p animal.name」と書けば「”サル”」と書き出される

<インスタンス変数で、attr_accessorを使用した場合>

class Animal
  attr_accessor :name
end

と定義する事で、「animal = Animal.new」とインスタンスを作成した後、「animal.name = “サル”」と代入する事で、「p animal.name」と書けば「”サル”」と書き出される

【継承】

既存のクラスを元に新しいクラスを作成すること

<定義の仕方>

class クラス名 < 継承したいクラス名

<使用例>

class Animal
  def self.greet
    p "こんにちは!Animalです!"
  end
end

と定義し、

class Dog < Animal
end

と継承し「Dog.greet」を書けば、「"こんにちは!Animalです!”」と書き出される
※継承後にクラス独自のメソッドも同じように定義可能

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

Ruby , Ruby on Railsの根っこを理解する為に最低限必要な基礎知識① 〜基礎用語〜

【ディレクトリとファイルの作成・実行コマンド】

ディレクトリの作成

$ mkdir 作りたいディレクトリ名

ファイルの作成

$ touch 作りたいファイル名.rb

ファイルの実行

$ ruby 実行したいファイル名.rb

※「.rb」はRubyの拡張子

【基礎用語】

データ型

オブジェクトが文字列なのか数値なのかを区別する種類のこと
※例”1”は文字列のデータ型なので、.to_iメソッドで整数に変換し計算を行う

文字列

データ型の一種で、テキストデータ

数値

データ型の一種で、計算を行える数字データ

変数

文字列や数値、計算などを定義されたデータ
※「変数名 = 数値や計算など」で代入し、「p 変数名」などにて、呼び出したり、出力する

<使用例>

「date = 2020」と定義すれば「p = date」と書いた時「2020」と出力される
※「=」は等しいと言う意味ではなく、左の値に右の値を代入する(定義する)と言う意味となる

配列

同じ括りのオブジェクトを一つにまとめる事
「変数名 = [“A”,”B”,”C”]」などで定義し、「p 変数名[インデックス番号]」などで呼び出したり、出力する

<使用例>

「fruits = ["リンゴ", "メロン", "バナナ”]」と定義した時、「p fruits[1]」と書いて、「メロン」と出力される
※インデックス番号は0から始まる番号

ハッシュ

まとめられた値のデータ型につける事ができる名前の事
「変数名 = [データ1, データ2, データ3]」などと複数のデータがまとめられた時に何を意味するか分からなくなってしまわないよう「変数名 = { ハッシュ名1: データ1, ハッシュ名2: データ2, ハッシュ名3: データ3}」とラベルをつける事で、p 変数名[:ハッシュ名]と呼び出せるように出来る

<使用例>

「fruits = { name: 'りんご', price: 100 }」と定義する事で、「p fruits[:name]」と書けば「’りんご’」と返ってくる

【メソッド】

プログラムが実行された時に行われる処理の事
デフォルメされたものもあれば、自分で作る事もできる関数のようなもので、「.」に続けて記載する事で、呼び出し使う事ができる

<使用例>

「length」と言うデフォルメされている文字数を返すメソッドを使う場合、「p "りんご".length」と記載する事で、「3」が返ってくる
※メソッドを呼び出すオブジェクト(例で言う”りんご”)のことをレシーバと呼び、メソッドを実行して返ってくる結果を返り値と呼ぶ

<自分で定義する方法>

「def メソッド名」でメソッドを定義する宣言を行い、処理を書いた最後に「end」で括り、メソッド名を書いて呼び出し使用する事ができる

<自分で定義する時の使用例>

def apple
  p "りんご"
end

と定義した場合「apple」と書くと、「りんご」が返ってくる

<メソッドに引数を渡す方法>

「def メソッド名(引数名)」と宣言し、「p 引数名」と処理を書く事で、「メソッド名(引数名)」を記載した際に入れた引数名をメソッドが受け取り出力する

<メソッドに引数を渡す時の使用例>

def fruits(name)
  p name
end

と定義した場合「fruits(“りんご”)」と書けば“りんご”と、fruits("バナナ”)と書けば”バナナ”と、それぞれ返ってくる

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

Ruby , Ruby on Railsの根っこを理解するための基礎知識① 〜基礎用語〜

【ディレクトリとファイルの作成・実行コマンド】

ディレクトリの作成

$ mkdir 作りたいディレクトリ名

ファイルの作成

$ touch 作りたいファイル名.rb

ファイルの実行

$ ruby 実行したいファイル名.rb

※「.rb」はRubyの拡張子

【基礎用語】

データ型

オブジェクトが文字列なのか数値なのかを区別する種類のこと
※例”1”は文字列のデータ型なので、.to_iメソッドで整数に変換し計算を行う

文字列

データ型の一種で、テキストデータ

数値

データ型の一種で、計算を行える数字データ

変数

文字列や数値、計算などを定義されたデータ
※「変数名 = 数値や計算など」で代入し、「p 変数名」などにて、呼び出したり、出力する

<使用例>

「date = 2020」と定義すれば「p = date」と書いた時「2020」と出力される
※「=」は等しいと言う意味ではなく、左の値に右の値を代入する(定義する)と言う意味となる

配列

同じ括りのオブジェクトを一つにまとめる事
「変数名 = [“A”,”B”,”C”]」などで定義し、「p 変数名[インデックス番号]」などで呼び出したり、出力する

<使用例>

「fruits = ["リンゴ", "メロン", "バナナ”]」と定義した時、「p fruits[1]」と書いて、「メロン」と出力される
※インデックス番号は0から始まる番号

ハッシュ

まとめられた値のデータ型につける事ができる名前の事
「変数名 = [データ1, データ2, データ3]」などと複数のデータがまとめられた時に何を意味するか分からなくなってしまわないよう「変数名 = { ハッシュ名1: データ1, ハッシュ名2: データ2, ハッシュ名3: データ3}」とラベルをつける事で、p 変数名[:ハッシュ名]と呼び出せるように出来る

<使用例>

「fruits = { name: 'りんご', price: 100 }」と定義する事で、「p fruits[:name]」と書けば「’りんご’」と返ってくる

【メソッド】

プログラムが実行された時に行われる処理の事
デフォルメされたものもあれば、自分で作る事もできる関数のようなもので、「.」に続けて記載する事で、呼び出し使う事ができる

<使用例>

「length」と言うデフォルメされている文字数を返すメソッドを使う場合、「p "りんご".length」と記載する事で、「3」が返ってくる
※メソッドを呼び出すオブジェクト(例で言う”りんご”)のことをレシーバと呼び、メソッドを実行して返ってくる結果を返り値と呼ぶ

<自分で定義する方法>

「def メソッド名」でメソッドを定義する宣言を行い、処理を書いた最後に「end」で括り、メソッド名を書いて呼び出し使用する事ができる

<自分で定義する時の使用例>

def apple
  p "りんご"
end

と定義した場合「apple」と書くと、「りんご」が返ってくる

<メソッドに引数を渡す方法>

「def メソッド名(引数名)」と宣言し、「p 引数名」と処理を書く事で、「メソッド名(引数名)」を記載した際に入れた引数名をメソッドが受け取り出力する

<メソッドに引数を渡す時の使用例>

def fruits(name)
  p name
end

と定義した場合「fruits(“りんご”)」と書けば“りんご”と、fruits("バナナ”)と書けば”バナナ”と、それぞれ返ってくる

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

Ruby , Ruby on Railsの根っこを理解する為に最低限必要な基礎用語

データ型

オブジェクトが文字列なのか数値なのかを区別する種類のこと

※例”1”は文字列のデータ型なので、.to_iメソッドで整数に変換し計算を行う
文字列:データ型の一種で、テキストデータ

数値

データ型の一種で、計算を行える数字データ

変数

文字列や数値、計算などを定義されたデータ

「変数名 = 数値や計算など」で代入し、「p 変数名」などにて、呼び出したり、出力する
<例>「date = 2020」と定義すれば「p = date」と書いた時「2020」と出力される

※「=」は等しいと言う意味ではなく、左の値に右の値を代入する(定義する)と言う意味となる

配列

同じ括りのオブジェクトを一つにまとめる事

「変数名 = [“A”,”B”,”C”]」などで定義し、「p 変数名[インデックス番号]」などで呼び出したり、出力する
<例>「fruits = ["リンゴ", "メロン", "バナナ”]」と定義した時、「p fruits[1]」と書いて、「メロン」と出力される

※インデックス番号は0から始まる番号

ハッシュ

まとめられた値のデータ型につける事ができる名前の事

「変数名 = [データ1, データ2, データ3]」などと複数のデータがまとめられた時に何を意味するか分からなくなってしまわないよう「変数名 = { ハッシュ名1: データ1, ハッシュ名2: データ2, ハッシュ名3: データ3}」とラベルをつける事で、p 変数名[:ハッシュ名]と呼び出せるように出来る
<例>「fruits = { name: 'りんご', price: 100 }」と定義する事で、「p fruits[:name]」と書けば「’りんご’」と返ってくる

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

RSpec FactroyBot sequence

はじめに

RSpecテストを記述中sequenceというものを使ったのでメモしていきます。

Rails 6.0.3.4
ruby 2.6.3p62
RSpec 3.10

sequenceの使い方

まず基本的にはユニーク制約に接触しないために使うことが多いようです。
自分もuserというモデルのemailカラムをユニークにしており、メールアドレスは既に存在してますというバリデーションエラーが出た時に使いました。 書き方は

FactoryBot.define do
    factory :user do
        sequence(:email) { |n| "kato#{n}@gmail.com"}
        name  {"加藤"}
        password {"password"}
        password_confirmation {"password"}
        level { 0 }
    end

userをbuildまたはcreateするたびにnの数が増えていき一意なメールアドレスのユーザーを作成できます。

 create(:user).email # "kato1@gmail.com"
 create(:user).email # "kato2@gmail.com"
 create(:user).email # "kato3@gmail.com"

最後に

まだまだ勉強中のため訂正などありましたご指摘いただけると幸いですm(__)m

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

【Ruby】Railsを利用して画像にリンクを貼りたい【link_to】

背景

Bootstrapを用いてリンクを装飾しようと意図したとき、適用されずあれ?ってなったので備忘録です

結論

「link_toメソッド内にclassを指定するオプションが存在した」

事象

そもそもなぜこの問題が起こったかですがa要素にclassを適用して、a要素の中にlink_toメソッドを記載していました。(以下のコード)

問題のコード

index.hetml
<a class="nav-link" href=""> 
 <%= link_to('tweetApp',"/") %> 
</a> 

実際に作成されたHTMLを見ると、、、

index.html
<a class="nav-link" href=""></a> 
<a href="/">tweetApp</a> 

あれ?a要素の中にlink_toメソッドを記載したはずなのに飛び出てますね

ここでおかしいと思い調べたところどうやらlink_toメソッド内にclassを指定してあげることでlink_toから生成されるa要素にcssが適用されるみたいです。

解決方法

書式
link_to(リンクテキスト, パス [, オプション, HTML属性 or イベント属性])

実際のコード(今回はtext-lihgtを使用しています)

index.html
<%= link_to("tweetApp","/",class: "text-light"%>

生成されたhtml

index.html
<li class="nav-item">
 <a class="nav-link" href="">tweetApp</a>
</li>

無事a要素にcssが適用されました。

参考

Railsドキュメント:ビューについて

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

【初学者向け】いいね機能をつける方法

はじめに

ブログ等のアプリケーションを作成する際に必要ないいね機能。
モデルとモデルを結びつける1:Nの関係が初学者の私には難しかったので、復習も兼ねて記事にしてみます。

こんな人に向けて

1.いいね機能を実装したい人。
2.Railsでアプリケーションを作成している人。
3.初学者(とりあえず機能を付け加えてみたい人)。

1.前提条件

・Userモデル作成済み(gem deviseで作成しています)
・Postモデル作成済み

2.いいね機能を作成する

2-1.いいねモデルとテーブルを作成

テーブルはuser_idとpost_idを作成します。
いいね機能というのは、「ある人がある投稿をいいねする」 といったものです。
つまり1つの「いいね」に対して、「ある人」と「ある投稿」の2つの情報が必要になります。
それらを結びつけるために下記モデル及びテーブルをつくります。
今回はFavoriteモデルを作成します。(モデル名は何でもOKです)

ターミナル
rails g model Favorite user_id:integer post_id:integer
rails db:migrate

2-2.コントローラの作成

いいね機能のコントローラを作成します。

ターミナル
rails g  controller Favorites create destroy

いいね機能には一覧ページや詳細ページはなく、いいねをつける・外すのみなので、createアクションとdestroyアクションのみを作成しています。

2-3.ルーティングの設定

ルーティングを設定します。

config/routes.rb
resources :favorites, only: [:create, :destroy]

createアクションとdestroyアクションのみしか作成していないので、
only:[:create, :destroy]  と加えました。

2-4.各モデル同士の関連付け

ここは初学者にとって難しく感じる部分かもしれません。
現在までに作成した、Userモデル・Postモデル・Favoriteモデルを繋げる作業をします。

ここで1:Nの関係といわれる考え方が登場します。
・1人のユーザーは複数(N)件の投稿をすることができる。
・1人のユーザーは複数(N)個のいいねをつけることができる。
・1件の投稿に対して複数(N)個のいいねがつけられる。

そしてこの関係性を各モデルに記述していきます。
1のモデルに has_many:Nモデル名(複数形)
Nのモデルに belongs_to:1モデル名(単数形)  です。

app/models/user.rb
has_many :posts, dependent: :destroy
has_many :favorites, dependent: :destroy

dependent: :destroy と記載しているのは、1のモデルが消えたときにそれと付随してNのモデルも消す処理をするためです。
(例えばユーザーが退会したときに、そのユーザーの投稿やいいねも一緒に消えるようにする処理)

app/models/post.rb
belongs_to :user
has_many :favorites, dependent: :destroy
app/models/favorite.rb
belongs_to :user
belongs_to :post

2-5.アクションの作成

2-2でcreateアクションとdestroyアクションを作成しました。
その中身を記述していきます。

app/controllers/favorites_controller.rb
  def create
    post = Post.find(params[:post_id])
    favorite = Favorites.new(post_id: post.id)
    favorite.user_id = current_user.id
    favorite.save
    redirect_to request.referer
  end

  def destroy
    post = Post.find(params[:post_id])
    favorite = current_user.favorites.find_by(post_id: post.id)
    favorite.destroy
    redirect_to request.referer
  end

createアクションを1行ずつ見ていくと、

post = Post.find(params[:post_id])

URLから受け取ったidをもとにPostモデルのデータを取得し、ローカル変数postに代入している。

favorite = Favorites.new(post_id: post.id)

先のローカル変数postで定義したPostモデルのデータのidをpost_idカラムに代入し、そのFavoriteモデルのデータが新しく作成され、それがローカル変数favoriteに代入している。

favorite.user_id = current_user.id

新しく作成されたFavoriteモデルのデータのuser_idは現在のユーザーのidとしている。

favorite.save

それを保存する。

redirect_to request.referer

処理をした後にどのページにリダイレクトするかを設定。(このように書くと遷移元のURLにリダイレクトします。)

このような流れで書きます。
destroyアクションもほぼ同じです。

favorite = current_user.favorites.find_by(post_id: post.id)

の部分は

favorite = Favorites.find_by(post_id: post.id)
favorite.user_id = current_user.id

を1行で書いているだけですので、意味は変わりません。
createアクションにも活かせます。

2-6.メソッドの作成

ビューで使うメソッドをここで作成します。
いいねボタンはいいねしている状態としていない状態によってアクションが変わります。

テレビで考えてみると、テレビがついているときに電源ボタンを押すと消えます。
逆に消えているときに電源ボタンを押すと付きます。

電源を押すという同じ行為ですが、現在の状態によってアクションが変わります。
この「現在の状態」を判別するためのメソッドを作成していきます。

app/models/post.rb
def favo?(user)
  favorites.where(user_id: user.id).exists?
end

メソッド名は何でも良いですが(user)の引数を設定しておきます。

favorites.where(user_id: user.id).exists?

Favoriteモデルのuser_idカラムに引数で設定するuserのidが存在するかどうかを判別し、true,falseで返してくれます。
.exists? は存在の判別をするメソッドです。

これでメソッドは完成しました。
ビューを作成したらいいね機能の完成です。

2-7.ビューの作成

最後はビューを作成します。
いいね機能を実装したいページによってコードを書く場所や書き方は異なりますが、今回は一覧ページに実装していきたいと思います。

app/controllers/posts_controller.rb
def index
  @posts = Post.all
end
app/views/posts/index.html.erb
<% @posts.each do |post| %>
    <% if post.favo?(current_user) %>
      <%= link_to favorites_path(post), method: :delete do %>
        ♥ <%= post.favorites.count %>
      <% end %>
    <% else %>
      <%=link_to favorites_path(post), method: :post do %>
        ♡ <%= post.favorites.count %>
      <% end %>
    <% end %>
<% end %>

いいね機能の部分だけ抜粋しました。(本来は投稿のタイトルや本文も表示されるようにビューに書く必要があります。)

<% if post.favo?(current_user) %>
  //いいねを消す処理
<% else %>
 //いいねをつける処理
<% end %>

2-6で作成したfavo?メソッドを使用します。
現在のユーザーがその投稿にいいねをしているかどうかを判別し、true(している)なら消す処理、false(していない)ならつける処理をするようにif文で条件分岐させています。

<%= link_to favorites_path(post), method: :delete do %>
   ♥ <%= post.favorites.count %>
<% end %>

.countメソッドを使うといいねされている数を表示させることができます。
♥と数字にリンクを設定しているため、上記のように書いています。

♥だけの場合は下記でOKです。

<%= link_to "♥" favorites_path(post), method: :delete %><%= post.favorites.count %>

これで完成しました。

3.最後に

今回いいね機能を実装しました。
これをもとにして次は非同期通信のいいね機能をつくっていきたいと思います。

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

tableタグについて

tableタグとは?

 HTMLでテーブル(表)を作成するために使用するタグ。実際にテーブルを作成する際は、以下のtableの子要素やブロック要素を併用して作成する。

tableの子要素 説明
thead テーブルの行(水平方向)をグループ化するタグ。このタグに囲まれた内容は表のヘッダ部分としてグループ化される。
tbody テーブルの行(水平方向)をグループ化するタグ。このタグに囲まれた内容は表のボディ部分としてグループ化される。
tfoot テーブルの行(水平方向)をグループ化するタグ。このタグに囲まれた内容は表のフッタ部分としてグループ化される。
ブロック要素 説明
tr テーブルの行部分(横方向)を指定するタグ。( table record )
th テーブルの見出しやタイトルとなるセルを指定するタグ。( table theme )
td テーブルのセル内容を作成を指定するタグ。( table date )

記述例

<table class="teble">
  <thead>
    <tr>
      <th class="header1">見出し1</th>
      <th class="header2">見出し2</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>内容1</td>
      <td>内容2</td>
    </tr>
  </tbody>
  <tfoot>
    <tr>
      <td>フッタ1</td>
      <td>フッタ2</td>
    </tr>
  </tfoot>
</table>

 以下のようになる。

見出し1 見出し2
内容1 内容2
フッタ2 フッタ1
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Rails] ビューの作成方法

役割

ビューの役割は「コントローラーから渡されたデータを利用して、Webサイト上での見た目を定義する」です。

ERB

ブラウザに表示できるのは、HTMLファイルだけですが、
データをHTMLでも使用したいときは、Rubyを埋め込むことができるERBという仕組みを使用します。

ERBは、HTMLにRubyの記述を埋め込むことができるテンプレートから、HTMLファイルを生成できるテンプレートエンジンです。

ERBは (Embedded Ruby)の略です。 ※Embedded = 埋め込み
拡張子は.erbを使う。

テンプレートエンジン

テンプレートエンジンとは、雛形となるテンプレートと、そのテンプレートにデータとなる記述を埋め込むことで、最終的に別のファイルとして生成できる仕組みのことです。
具体的には、コントローラーで定義したインスタンス変数を、ビューで使用できるようになります。

*記号の意味

<%=   %>  (イコール) ・・・ 記述内容を出力する。

book_controller.rb
def index
  @name = "Shun"
end
index.html.erb 
<%= @name %>

とすると、

html.rb
Shun

と出力されます。

※ ifなどを使用する場合
<%   %> (パーセントのみ) ・・・ Rubyコードを埋め込むが、表示はされない。

ERBファイル

RailsにおけるERBファイルは、○○.html.erbという名前で作成します。
○○には、対応するアクション名を記入。

Railsは、このERBファイルを元にHTMLファイルを書き出して、最終的にレスポンスに含めて返すことでブラウザに画面を表示させています。

ERBファイル作成方法

VS Codeで
app/views/〇〇ディレクトリを二本指でクリックし、〇〇.html.erbを作成しましょう。

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

ActiveJobと周辺知識についてまとめてみた

はじめに

ActiveJobを使う上での前提知識や周辺知識を深めようと思いまとめてみました。
用語の定義、メリデメ比較、スタイルガイドなどを中心に書いています。
具体的な実装方法は書いていません。

ActiveJobとは?

Active Jobは、ジョブを宣言し、それによってバックエンドでさまざまな方法によるキュー操作を実行するためのフレームワークです。
Rails4.2から導入されました。

cf. Active Job の基礎 - Railsガイド

用語の整理

用語 説明
ジョブ 端末ごとに見た一連の処理
例:定期的なクリーンアップ、請求書発行やメール配信
キュー 先に入れたものを先に出すというルールのデータ構造の1つ
先入先出(FIFO)と書かれることもある
エンキュー キューに要素を追加すること
デキュー キューから要素を取り出すこと
キューイング キューを用いて要素の管理を行うこと
キューイングシステム(ライブラリ) キューを持っているシステム
メッセージ キューに流れるデータの1単位
メッセージキューイング 異なるソフトウェア間でデータを送受信する手法の一つ
直接データを渡すのではなく一旦第三者のソフトウェアに預けることで、送信側も受信側も好きなタイミングで送受信処理をおこなうことができるようにする方式

参考

キューイングライブラリ(一例)

  • delayed_job
  • sidekiq
  • Resque

ActiveJobを使う/使わない場合のメリデメ比較

ActiveJobあり ActiveJobなし
メリット キューイングライブラリへの依存が減る
- ライブラリ固有のコードを書かずに済む
- ライブラリ変更時にビジネスロジックの書き換えが不要
ライブラリ固有の機能を使える(ものもある)
デメリット ライブラリ固有の機能が使えない ライブラリ変更時にコードの修正範囲が広い

参考

リリースノート

スタイルガイド

Rails: Active Jobスタイルガイド(翻訳)|TechRacho(テックラッチョ)〜エンジニアの「?」を「!」に〜|BPS株式会社

※ 以下のリストは、実装・レビュー時にチェックしやすいように、スタイルガイドの項目を質問文に言い換えたものです

  • 引数に(idではなく)ActiveRecordモデルを渡しているか?
  • 引数を破壊的に変更していないか? 処理待ちのジョブがキューに残っていないか?
  • 引数は十分少ないか?
  • タスクの緊急度に応じたキューを指定しているか?
    • 重い処理や緊急でない処理を、緊急性の高いキューに入れていないか?
    • 優先度の高いキューを通常(の優先度)のキューに入れていないか?
  • 冪等性は保証されているか? あるいは許容できる理由はあるか?
  • デプロイ中にジョブが実行されても問題ないか?
  • 原子性は保証(成功 or 何もしないように実装)されているか?
  • ジョブ中で(利用厳禁である)スレッドを使っていないか?
  • ジョブのリトライメカニズムを(隠蔽・切り出しせず)ジョブ中に書いているか?
  • バッチ処理にリトライは付いているか?
  • トランザクションのコミットが完了したジョブのみキューに入れているか?
  • ビジネスロジックをジョブ中に書いていないか?
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む