- 投稿日:2020-12-07T23:30:49+09:00
flashメソッドの角括弧の中身はなんでもいい件
はじめに
先日、
flashメソッド
を使って、メッセージ送信成功時に画面に「成功しました」的なメッセージを表示させる方法を投稿したが、その後に調べていると驚くべき発見があったので、紹介。前回の投稿では
コントローラーとビューに以下のように記述するように紹介した。
flash[:success] = '送信されました'<%= flash[:success] %>そのときは、
:success
に意味があると思っていた。しかし、:success
には特別な意味がなかった。その時には、他にも:notice
を角括弧で入れている場合もあり、
成功時には、:success
、
失敗時には、:notice
と思い込んでいた。実は
角括弧の中身は、コントローラーとビューが一致さえしていれば、どんな文字でもOK!もちろん、
:a
でもいい。(まぁ、わかりやすい命名の方が読みやすいが…)
あくまで、ハッシュのような形で、それをビューで表示させているようだった。
- 投稿日:2020-12-07T23:04:45+09:00
ポートフォリオ作成記録 第4章の進捗
カスタムヘルパーを作ろう
ヘルパー関数はC#やC++での学習でも出会ったことがあります。(ヘルパー関数は他の関数の処理を助ける便利な関数と覚えた記憶があります。よし!この場合の私は、このことはちゃんと復習が必要ですね!読み流してください。笑)
チュートリアルの内容だと組込関数以外で自作の関数を指しているみたいです。Railsでインタプリタ(逐次翻訳)を体験
「rails console」でインタプリタモードになるので、Rubyを練習する事ができます。あくまでも、簡単なプログラムを組んで試す事ができるので、その時は便利かも知れません。チュートリアルでは文字列操作でRubyの変数を展開(ダブルクオートの中で#{変数}を使うと変数が組み込める、文字列は、+の記号で文字列を結合するのと同じですね!)
Rubyのクオートで処理結果が異なる
インタプリタでRuby言語の記述を学んで文字列のクオートの意味と動作違うのでメモです。
シングルの文字列だと変数展開(#{})ができない。今日から進んだ所までを投稿しようと思います
これまでは、章が完了する事に投稿しようと思いましたが、復習の意味もこめてその都度、記述して投稿しようと思います。また、不明な言葉や内容の部分も記録してスクラップ記事みたいに集めて2周目の復習時に調べて行うのにいいのかとも思っています。色々、試行錯誤して投稿していこうと思います。
自分が知っている専門用語
目的=今回は自分の知っている用語をテストの意味で残すのが目的です。確認するのが楽しみ!
・組込関数
他のクラスを呼び出して使わない関数のこと。
呼び出せる理由は、コンピュータ向けの言語に変換されているファイルをプログラム実行時に組み込ませているので(リンカ)記述が不要。Cのビルドの工程を見ると知れます。興味があれば調べてどうぞ!
知らないと、組込エンジニアがドヤってくるよ!(私の体験談です。笑)
- 投稿日:2020-12-07T22:03:41+09:00
【備忘録】deviseの導入
deviseとは?
Railsアプリケーションに認証機能(ユーザー登録機能、ログイン機能、ログアウト機能)を実装することができるgemです。数回のコマンドを実行するだけで簡単に認証機能を実装することができます。
導入の流れ
1. Gem導入
Gemfileに追記。
Gemfile.gem 'devise'bundle install実行。
bundle install2. インストール
アプリケーションにdeviseをインストール。ここは忘れがちなので注意!
rails g devise:install3. Userモデルの作成
rails g devise userモデル作成と同時に、マイグレーションファイルやルーティングも自動的に作成されます。
4. 必要に応じてカラムを追加
deviseを介してモデルを作成した場合、emailとpasswordはデフォルトで用意されているので、他に必要なカラムがある時は追加します。
5. マイグレーション実行
マイグレーションファイルの内容をテーブルに反映させます。
rails db:migrate6. ビューの作成
新規登録・ログイン用のビューを作成します。
rails g devise:viewsこんな感じのビューが出来上がります⬇️
新規登録画面
ログイン画面
- 投稿日:2020-12-07T21:46:44+09:00
いいね機能を非同期で実装する方法
はじめに
前回いいね機能を実装しました。
ただ、いいねをするたびにページがリダイレクトされてしまいます。
そこで非同期通信という通信方法を使い、リダイレクトすることなくにビューに反映できるようにしていきたいと思います。
少しだけjQueryの知識が必要になります。こんな人に向けて
1.いいね機能を実装済みの人。
2.非同期通信での実装をしたい人。1.前提条件
・いいね機能を実装済みであること(実装していない人は こちら)
(前回のいいね機能を改良していきます)2.実装準備
非同期通信を実装するにあたって、新たなgemをインストールする必要があります。
Gemfilegem "jquery-rails"ターミナルbundle installapp/assets/javascripts/application.js//= require jquery (←この行を新しく記載) //= require rails-ujsjQueryという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.rbdef 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_to
にremote: 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.erb
とdestroy.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.rbdef 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を使うことです。
少しだけややこしくなりますが、作る流れは基本的に一緒だと思います。
- 投稿日:2020-12-07T20:16:04+09:00
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
- 投稿日:2020-12-07T20:04:54+09:00
RailsエンジニアがGraphQLと比較してみた
はじめに
こんにちは!榊原です。
トレタに入社して1年が経ちました。
この1年はとても変化が多くあった年だったと感じています。特に今年の1月から会社全体でフルリモート化や、
DXを意識した飲食店での自分のスマホでオーダーできるシステムや予約時に座席を指定して予約できるシステムなど、
時代の変化に対応した様々な新規サービスを発表した年でもあります。発表したサービスの中にはまだ市場に受け入れられるか仮設段階のものも数多くありました。
エンジニアの人数も限りがある中で、最速かつ柔軟な変化を求められる開発を進めるに当たって、
最近弊社で使われ始めているのがGraphQLです。今回はそんなGraphQLを弊社でも創業当初から運用されてきたRuby on Railsとの比較を中心とした説明とさせていただきます。
GraphQLを説明をするに当たって、グラフ理論は欠かせないのですがそちらは他の記事が多数存在するため今回は割愛させていただきます。
対象となる読者
- Railsは使用していてもGraphQLは触ったことがないけど触ってみたい
- サーバーサイドを担当していたけど、業務で使用しなければならなくなった
対象クライアント、バージョン
- GraphQLエンジン v1.3.3
- DB: PostgreSQL v13.1
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の説明
今回使用するのは下記の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
実際にサービスを作成していると複数のテーブル情報を一度に取得したいというケースは多くの場面で遭遇すると思います。
ここでは、シンプルに
persons
とphotos
のデータを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
persons
とphotos
をそれぞれ別で取得を行いました。
ですが実際にはリレーションに紐づく値のみを取得したいというケースの方が多いかと思います。次は
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.saveGraphQL
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.destroyGraphQL
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のドキュメントを読んでみてください。
最後に
弊社ではGraphQLを仮説検証段階の、速度感をもったサービス開発で少しづつ取り入れ始めています。
そんな技術もサービスも一緒に検証したいエンジニアを募集しています。
カジュアルな面談もありますので、一度下記のURLから確認してみてください!
https://corp.toreta.in/recruit/midcareer/
- 投稿日:2020-12-07T20:04:54+09:00
RailsエンジニアがGraphQLを触って比較してみた
はじめに
こんにちは!榊原です。
トレタに入社して1年が経ちました。
この1年はとても変化が多くあった年だったと感じています。特に今年の1月から会社全体でフルリモート化や、
DXを意識した飲食店での自分のスマホでオーダーできるシステムや予約時に座席を指定して予約できるシステムなど、
時代の変化に対応した様々な新規サービスを発表した年でもあります。発表したサービスの中にはまだ市場に受け入れられるか仮設段階のものも数多くありました。
エンジニアの人数も限りがある中で、最速かつ柔軟な変化を求められる開発を進めるに当たって、
最近弊社で使われ始めているのがGraphQLです。今回はそんなGraphQLを弊社でも創業当初から運用されてきたRuby on Railsとの比較を中心とした説明とさせていただきます。
GraphQLを説明をするに当たって、グラフ理論は欠かせないのですがそちらは他の記事が多数存在するため今回は割愛させていただきます。
対象となる読者
- Railsは使用していてもGraphQLは触ったことがないけど触ってみたい
- サーバーサイドを担当していたけど、業務で使用しなければならなくなった
対象アプリケーション
- GraphQLエンジン v1.3.3
- DB: PostgreSQL v13.1
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の説明
今回使用するのは下記の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
実際にサービスを作成していると複数のテーブル情報を一度に取得したいというケースは多くの場面で遭遇すると思います。
ここでは、シンプルに
persons
とphotos
のデータを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
persons
とphotos
をそれぞれ別で取得を行いました。
ですが実際にはリレーションに紐づく値のみを取得したいというケースの方が多いかと思います。次は
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.saveGraphQL
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.destroyGraphQL
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のドキュメントを読んでみてください。
最後に
弊社ではGraphQLを仮説検証段階の、速度感をもったサービス開発で少しづつ取り入れ始めています。
そんな技術もサービスも一緒に検証したいエンジニアを募集しています。
カジュアルな面談もありますので、一度下記のURLから確認してみてください!
https://corp.toreta.in/recruit/midcareer/
- 投稿日:2020-12-07T19:12:06+09:00
Docker開発環境でCapybara, FactoryBotを使ったテスト環境を構築追加する
概要
docker開発環境でRSpecテスト環境を構築するやり方の備忘録としてまとめました。
独学なので間違っている部分があると思いますが、その時はご指摘頂けると嬉しいです。以下の記事を参考にしました。
https://qiita.com/at-946/items/e96eaf3f91a39d180eb3
https://qiita.com/na-tsune/items/91630257294aa0ea4fc81.docker-compose.ymlの編集
docker-compose.ymlversion: '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を追記
Gemfilegroup :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' enddockerの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]
- 投稿日:2020-12-07T19:05:06+09:00
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の場合にそれ以降のコードを読まないように実装できる。もし何か間違い等あれば教えていただきたいです!
- 投稿日:2020-12-07T19:04:15+09:00
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.rbrequire '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_methods5.redis-actionpackをアップデートしてみる
上記の
redis-rack
のアップデート実行後、
こちらの記事
https://github.com/redis-store/redis-rack/pull/50#issuecomment-567649953
を参考に
bundle update redis-actionpack
これでサーバー再起動後、Sidekiqの管理画面の500エラーが解消し、テストも通った。
まとめ
redis-rack
とredis-actionpack
のアップデートで解決しました。
- 投稿日:2020-12-07T18:49:32+09:00
Rails newが上手くいかない時の対処法
今日1時間ほどコマンドプロンプトでrails newをしても必要なファイルが作成されず、苦戦したのでその対処法を残しておきます。
誰かの参考になれば嬉しいです。
では、本題ですがコマンドプロンプトでrails newを実行するとこんな画面が表示されてばかりで、欲しいファイルが作成されませんでした。
gemfileでdeviseをインストール出来ないことからこの異変に気付いたのですが、最初はずっとgemfile側の問題だと思っていたのですが、全く改善できず。。。そこで、最初に上手くいっていたアプリがあったのでそれを参考にしようと思ったのですが、まさか一番最初の時点でミスっているなんて思わずドツボにハマり、1時間ちょっと使いました(笑)
で、改めてこのコマンドプロンプトの表示を見てみるとgitのところで止まっていることに気づき、ググってみるとgitのコマンドがコマンドプロンプト上で使えるようになっていないからだと分かりました!!
だから、解決策としてはrails new アプリ名 -Gと後ろに-Gをつけてgitをスキップすれば解決することが判明し、無事解決することが出来ホッとしているところです。
gitをwindowsのコマンドプロンプトで使えるように設定しておけばよかった。。。
- 投稿日:2020-12-07T18:34:52+09:00
Railsについて初心者なりに調べてみた
ドキュメント
Ruby on 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開発フレームワークのシェアと推移
djangoとlaravelがトレンド上昇している。
ruby on rails は2011年以降、下降している。
Ruby on rails のトレンド下降している要因
Twitterが、Ruby on RailsからJavaVMへ移行する理由
Twitterの膨大化したアクセスを、railsで構築されたシステムよりもJavaVMの方が速やかに処理できる。
→大規模システム開発で使われるケースが世界的に減っている。
なぜ一時期、一世を風靡したRuby on railsが、「railsはもう終わった」と言われるようになったのか?
その一部の背景を上記で説明しました。以下は具体的にかかれている記事
ただ日本ではスタートアップ中心に仕事がまだまだたくさんある。
例、Cookpad, Gunosy, 食べログ, Freee, Crowdworks
開発環境
- ローカル
- Virtual Box
- Docker
- Cloud9(AWS)
開発の流れ(ローカル環境)
基本的にMVCモデルの設計に沿って、ファイルを作成する。
参考文献
- 投稿日:2020-12-07T18:13:04+09:00
【Rails】チャットの吹き出しの向きを変える方法
本記事は 駆け出しエンジニアの第一歩!AdventCalendar2020 7日目の記事です。
作りたいもの
下図のように、自分のメッセージと自分以外のメッセージでユーザーアイコンの位置や吹き出しの向きが変わるチャット機能を作ります。
前提条件
- チャット機能自体はできている
- bootstrap4がインストールされている
やることまとめ
- 自分用の吹き出しと相手用の吹き出しのCSSクラスを用意する
- Viewファイルにて、メッセージごとに自分or自分以外のメッセージかを判定する
- それぞれの判定ごとに適用する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 %>終わりに
今回は私個人のアプリで実際に実装したものを題材にしたので、人によってはビューの書き方は異なってくると思います!
ご参考になれば幸いです…!
参考サイト
- 投稿日:2020-12-07T16:14:05+09:00
railsテスト駆動する理由
railsのテスト駆動に関する参考になった記事があるので共有
https://teratail.com/questions/121833?link=qa_related_pc_sidebarテストをする理由は今のコードと自分の認識を一致させることだったということ。
user_test.rbclass 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 endassert_notはassert_notは引数の結果がfalseの時にテストが成功とする命令
つまり現時点でバリテーションがかかれていないため、@user.validがtrueとなるため、テストは失敗。
今のコードと自分の認識はあっていたということになる。
じゃあバリテーションを書こう。
こうなるわけなのか。。。なるほど。
テストって意味あるの?って疑問だったが、効率良く開発を進めていく上で必要不可欠なんだと理解した。(1) 仕様を先に決める(頭の中だけでもいいし、紙に書いたりしてもいい)
(2) 仕様をテストとして記述する
(3) 仕様を満たすために実装するこのステップが大事なんですね。
めちゃめちゃ参考になりました。テストの書き方とかも勉強しないとな
- 投稿日:2020-12-07T16:06:13+09:00
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です!”」と書き出される
※継承後にクラス独自のメソッドも同じように定義可能
- 投稿日:2020-12-07T15:47:36+09:00
rails マイグレーションファイルあれこれ
rails generate model User name:string email:string
Userのモデルをnameはstring型で、emailはstring型で作成という意味。
今までrails g model User のみをターミナルで入力してマイグレーションファイルをいじっていたので、このやり方は時短になるなあとおもった以下マイグレーションファイル
XXXXXXXXXXXXX_create_users.rbclass CreateUsers < ActiveRecord::Migration[6.0] def change create_table :users do |t| t.string :name t.string :email t.timestamps end end endchangeメゾットはcreate_tableというrailsのメゾットを呼び出し
create_tableメソッドはブロック変数を1つ持つブロックを受け取り,ここでは(“table”の頭文字を取って)t
ブロックの最後の行t.timestampsは特別なコマンドで
created_atとupdated_atという2つの「マジックカラム(Magic Columns)」を作成
$ rails db:migrate
でマイグレーションファイルが実行される。
実行されたマイグレーションファイルはいじっても更新されない
マイグレーションファイルが実行される前に戻りたいときは
$ rails db:rollback
このコマンドで戻すことができる
- 投稿日:2020-12-07T15:40:22+09:00
コンソールでDelayed::Jobを実行する手順
ローカル開発中にたまに使うときに忘れがちなので備忘録として。
以下でキューに入ったjobを実行可能
Delayed::Job.find(x).invoke_job実行した後はレコード削除
Delayed::Job.find(x).destroy以上、
- 投稿日:2020-12-07T15:27:18+09:00
【Rails Chart グラフ】Railsで、投稿に連動した美しいグラフ(レーダーチャート)を作ってみよう
【Rails Chart グラフ】Railsで、投稿に連動した美しいグラフを作ってみよう
Railsで、例えば
・投稿した成績をグラフ化したい時
・勉強時間をグラフ化したい時
など、様々な場面で、グラフを生成したいことがあるかと思います!そこで今回は、chart.jsを使って、グラフを生成し、投稿の情報と連動させてみたいと思います!
ちなみに、カラムは
Postモデルの
・title(投稿のタイトル)
・rate(グラフの観点1個目)
・kindness(グラフの観点2個目)
・sadness(グラフの観点3個目)
・bitterness(グラフの観点4個目)
を用意し、それぞれの観点からの点数をグラフ化したレーダーチャートを実装します実装までのステップ
1:chart.jsをCDN経由でRailsアプリに導入
2:グラフ生成のコード記述
3:ちょこっとコードいじって投稿と連動させるそれでは張り切っていきましょう!
1:chart.jsをCDN経由でRailsアプリに導入
こちらからCDNを作成しましょう!
タグにscriptを記述しましょう!
そして、view > layouts > application.html.erbの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>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>グラフの観点を増やしたい時
index.html.erbdata: { labels: ["Rate", "Kindness", "Sadness", "Bitterness"],ここに観点を追加し、
index.html.erbdata: { data: [40, 42, 42, 43]ここにもデータの値を追加してください。
データの数値の最小値、最大値を変えたい時
index.html.erboptions: { scale: { ticks: { suggestedMin: 0, suggestedMax: 100 } } }ここの
suggestedMin: 0,
suggestedMax: 100
値を変えてあげてください!?
- 投稿日:2020-12-07T15:23:15+09:00
ローカルで開発したアプリケーションをS3、EC2、RDSを使ってとりあえず動かしてみる
はじめに
こちらの記事で作成したアプリをAWSのサービスを使ってとりあえず動くようにしてみる記事です。
おおまかな流れ
今回はAWSのS3、EC2、RDSのサービスを使ってとりあえずrailsサーバーを立ち上げてアプリケーションが閲覧できる状態に持っていきたいと思います。
- S3のバケット作成、EC2インスタンス立ち上げ、RDSデータベース作成
- EC2にアプリのソースをクローン、環境構築
- EC2上でrailsサーバー立ち上げて実際に見れることを確認
今回の作業用のIAMユーザー作成
AWSのルートユーザーしかもっていなかったので、今回の作業用にIAMユーザーを作りました。(IAMユーザー自体は作らなくてもルートユーザーの操作から作業を進めることはできますが、作業に必要な権限のみを持ったIAMユーザーを作るのがセオリーです。)
RDS、EC2、S3それぞれにフルアクセスできるグループを作成して、グループ内にユーザーを追加しました。S3バケット作成
ローカルで
public/images
配下に置いていた全画像をS3バケットにアップロードします。
今回はS3上に置いた画像のパスを直接指定してアプリ上で表示させたかったので、アップロードの際のアクセスコントロールリストのアクセス許可をパブリックにしています。
これでS3上の画像パスを直接指定することで画像を表示できます。
public/images/
配下を指定していたview側の画像パスもS3のパスを指定するように変更しておきます。
今回はS3アドレスドメインをヘルパ関数で定義しておいて、viewの画像パス指定箇所に関数を呼び出す形で対応しました。# application_helper.rb def aws_s3_path 'https://rails-persona5.s3-ap-northeast-1.amazonaws.com' endEC2インスタンス起動
無料枠で作成できる
t2.micro
でEC2インスタンスを作成しました。
EC2インスタンス作成は特に必要な詳細設定等はありませんでしたが、パブリックDNSを指定してブラウザで開けるようにしたかったので、インスタンスが属するセキュリティグループのインバウンドルールを編集する必要がありました。
デフォルトで設定されているSSHルールに加えて、railsサーバーを起動するポート3000に対して0.0.0.0/0
からのアクセスを許可します。(0.0.0.0は任意のIPv4アドレス)
これでパブリックDNSのポート3000を指定してEC2にブラウザでアクセスできます。RDSデータベースの作成
無料利用枠でRDSデータベースを作成しました。エンジンはローカル開発環境に合わせてmysqlを設定しています。
EC2インスタンスからRDSにアクセスできるようにするために、RDSデータベースが属しているセキュリティグループに関してもインバウンドルールを設定する必要がありました。MYSQL/Auroraルールを追加して、ソースにはEC2が属するセキュリティグループ(今回は
launch-wizard-2
)を指定します。EC2が属するセキュリティグループに、データベースが属するセキュリティグループへのアクセスを許可することになります。
これでEC2からRDSにアクセスできます。EC2上でrailsサーバー起動
EC2インスタンスにソースをクローン、
database.yml
にRDSデータベースのホスト名、ユーザー名、パスワードを設定して、マイグレーションを実行後、ローカルのデータベース上のデータをダンプしてRDSデータベースにリストアしてデータの準備をします。
またrailsサーバー起動時にBlocked hostエラーが出たので対応しました。(実はこの他にもgemのバージョンやyarnのインストールなどいろいろつまづきました。このあたりの環境構築はdockerを利用すれば簡単になるらしいので勉強していきたいと思います。)
全ての準備が整ったら以下のコマンドでrailsサーバー起動です。
bundle exec rails s -b 0.0.0.0railsサーバーを起動したらEC2のパブリックDNSの3000ポート指定してブラウザからアクセスしてみます。
画像についても問題なく表示できているようです。
さいごに
今回の作業はここまでです。AWSのサービスを実際に触ることで理解を深めることができました。特にセキュリティグループの考え方については作業前の理解がほぼほぼ0だったので勉強になりました。
EC2上で実際にrailsサーバーを立ち上げる時に環境構築でいろいろつまづいたので、dockerのコンテナを利用したソース管理を勉強していきたいですね。また今回作った環境はローカルの開発環境をAWSサービス上にコピーしただけなので、railsサーバー起動時にも開発(development)環境として立ち上げているのみです。実際にwebアプリケーションを運用していく際には本番(production)環境を構築しなければならないので、そちらの構築方法に関しても勉強していきたいです。
- 投稿日:2020-12-07T14:41:57+09:00
serializerを使って紐付けされたモデルの値を簡単に取る方法
概要
プログラミング初心者がRailsでAPIを作るときの発見を書いてみました
誰かの参考になれば幸いですやりたいこと
comment_serializerでcommentモデルに紐づいているuserモデルの中のaccountnameだけをjsonで渡したい
model同様にhas_manyなどを使用して書く
comments_serializer.rbclass CommentSerializer < ActiveModel::Serializer attributes :id, :content has_many :user endこれでも取ることができるがuser_serializerファイルの作成やattributesの記述だったり色々と面倒
もっと簡単に値を取得したい!!objectを使用する
comments_serializer.rbclass CommentSerializer < ActiveModel::Serializer attributes :id, :content, :accountname def accountname object.user.accountname end endobjectを使うことでuser_serializerファイル作成も行うことなく値を取ることができる
- 投稿日:2020-12-07T13:28:17+09:00
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です!”」と書き出される
※継承後にクラス独自のメソッドも同じように定義可能
- 投稿日:2020-12-07T13:28:17+09:00
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("バナナ”)と書けば”バナナ”と、それぞれ返ってくる
- 投稿日:2020-12-07T13:28:17+09:00
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("バナナ”)と書けば”バナナ”と、それぞれ返ってくる
- 投稿日:2020-12-07T13:28:17+09:00
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]」と書けば「’りんご’」と返ってくる
- 投稿日:2020-12-07T13:22:30+09:00
RSpec FactroyBot sequence
はじめに
RSpecテストを記述中
sequence
というものを使ったのでメモしていきます。
Rails 6.0.3.4
ruby 2.6.3p62
RSpec 3.10
sequenceの使い方
まず基本的にはユニーク制約に接触しないために使うことが多いようです。
自分もuser
というモデルのFactoryBot.define do factory :user do sequence(:email) { |n| "kato#{n}@gmail.com"} name {"加藤"} password {"password"} password_confirmation {"password"} level { 0 } enduserを
build
またはcreate
するたびにn
の数が増えていき一意なメールアドレスのユーザーを作成できます。create(:user).email # "kato1@gmail.com" create(:user).email # "kato2@gmail.com" create(:user).email # "kato3@gmail.com"最後に
まだまだ勉強中のため訂正などありましたご指摘いただけると幸いですm(__)m
- 投稿日:2020-12-07T13:10:26+09:00
【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が適用されました。
参考
- 投稿日:2020-12-07T10:12:53+09:00
【初学者向け】いいね機能をつける方法
はじめに
ブログ等のアプリケーションを作成する際に必要ないいね機能。
モデルとモデルを結びつける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:migrate2-2.コントローラの作成
いいね機能のコントローラを作成します。
ターミナルrails g controller Favorites create destroyいいね機能には一覧ページや詳細ページはなく、いいねをつける・外すのみなので、createアクションとdestroyアクションのみを作成しています。
2-3.ルーティングの設定
ルーティングを設定します。
config/routes.rbresources :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.rbhas_many :posts, dependent: :destroy has_many :favorites, dependent: :destroydependent: :destroy と記載しているのは、1のモデルが消えたときにそれと付随してNのモデルも消す処理をするためです。
(例えばユーザーが退会したときに、そのユーザーの投稿やいいねも一緒に消えるようにする処理)app/models/post.rbbelongs_to :user has_many :favorites, dependent: :destroyapp/models/favorite.rbbelongs_to :user belongs_to :post2-5.アクションの作成
2-2でcreateアクションとdestroyアクションを作成しました。
その中身を記述していきます。app/controllers/favorites_controller.rbdef 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 endcreateアクションを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.rbdef 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.rbdef index @posts = Post.all endapp/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.最後に
今回いいね機能を実装しました。
これをもとにして次は非同期通信のいいね機能をつくっていきたいと思います。
- 投稿日:2020-12-07T09:55:03+09:00
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
- 投稿日:2020-12-07T09:39:59+09:00
[Rails] ビューの作成方法
役割
ビューの役割は「コントローラーから渡されたデータを利用して、Webサイト上での見た目を定義する」です。
ERB
ブラウザに表示できるのは、HTMLファイルだけですが、
データをHTMLでも使用したいときは、Rubyを埋め込むことができるERBという仕組みを使用します。ERBは、HTMLにRubyの記述を埋め込むことができるテンプレートから、HTMLファイルを生成できるテンプレートエンジンです。
ERBは (Embedded Ruby)の略です。 ※Embedded = 埋め込み
拡張子は.erbを使う。テンプレートエンジン
テンプレートエンジンとは、雛形となるテンプレートと、そのテンプレートにデータとなる記述を埋め込むことで、最終的に別のファイルとして生成できる仕組みのことです。
具体的には、コントローラーで定義したインスタンス変数を、ビューで使用できるようになります。*記号の意味
<%= %> (イコール) ・・・ 記述内容を出力する。
book_controller.rbdef index @name = "Shun" endindex.html.erb<%= @name %>とすると、
html.rbShun
と出力されます。
※ ifなどを使用する場合
<% %> (パーセントのみ) ・・・ Rubyコードを埋め込むが、表示はされない。ERBファイル
RailsにおけるERBファイルは、○○.html.erbという名前で作成します。
○○には、対応するアクション名を記入。Railsは、このERBファイルを元にHTMLファイルを書き出して、最終的にレスポンスに含めて返すことでブラウザに画面を表示させています。
ERBファイル作成方法
VS Codeで
app/views/〇〇ディレクトリを二本指でクリックし、〇〇.html.erbを作成しましょう。
- 投稿日:2020-12-07T08:36:28+09:00
ActiveJobと周辺知識についてまとめてみた
はじめに
ActiveJobを使う上での前提知識や周辺知識を深めようと思いまとめてみました。
用語の定義、メリデメ比較、スタイルガイドなどを中心に書いています。
具体的な実装方法は書いていません。ActiveJobとは?
Active Jobは、ジョブを宣言し、それによってバックエンドでさまざまな方法によるキュー操作を実行するためのフレームワークです。
Rails4.2から導入されました。用語の整理
用語 説明 ジョブ 端末ごとに見た一連の処理
例:定期的なクリーンアップ、請求書発行やメール配信キュー 先に入れたものを先に出すというルールのデータ構造の1つ
先入先出(FIFO)と書かれることもあるエンキュー キューに要素を追加すること デキュー キューから要素を取り出すこと キューイング キューを用いて要素の管理を行うこと キューイングシステム(ライブラリ) キューを持っているシステム メッセージ キューに流れるデータの1単位 メッセージキューイング 異なるソフトウェア間でデータを送受信する手法の一つ
直接データを渡すのではなく一旦第三者のソフトウェアに預けることで、送信側も受信側も好きなタイミングで送受信処理をおこなうことができるようにする方式参考
- 【一通りわかる】Linuxでのジョブの基本的な扱い方まとめ
- キュー (queue)とは|「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典
- キュー(待ち行列)とは - IT用語辞典 e-Words
- メッセージキューイング(MQ)とは - IT用語辞典 e-Words
- ストリーム処理を支えるキューイングシステムの選び方
キューイングライブラリ(一例)
- delayed_job
- sidekiq
- Resque
ActiveJobを使う/使わない場合のメリデメ比較
ActiveJobあり ActiveJobなし メリット キューイングライブラリへの依存が減る
- ライブラリ固有のコードを書かずに済む
- ライブラリ変更時にビジネスロジックの書き換えが不要ライブラリ固有の機能を使える(ものもある) デメリット ライブラリ固有の機能が使えない ライブラリ変更時にコードの修正範囲が広い 参考
リリースノート
- Ruby on Rails 4.2 リリースノート - Railsガイド
- Ruby on Rails 5.0 リリースノート - Railsガイド
- Ruby on Rails 5.1 リリースノート - Railsガイド
- Ruby on Rails 5.2 リリースノート - Railsガイド
- Ruby on Rails 6.0 リリースノート - Railsガイド
スタイルガイド
Rails: Active Jobスタイルガイド(翻訳)|TechRacho(テックラッチョ)〜エンジニアの「?」を「!」に〜|BPS株式会社
※ 以下のリストは、実装・レビュー時にチェックしやすいように、スタイルガイドの項目を質問文に言い換えたものです
- 引数に(idではなく)ActiveRecordモデルを渡しているか?
- 引数を破壊的に変更していないか? 処理待ちのジョブがキューに残っていないか?
- 引数は十分少ないか?
- タスクの緊急度に応じたキューを指定しているか?
- 重い処理や緊急でない処理を、緊急性の高いキューに入れていないか?
- 優先度の高いキューを通常(の優先度)のキューに入れていないか?
- 冪等性は保証されているか? あるいは許容できる理由はあるか?
- デプロイ中にジョブが実行されても問題ないか?
- 原子性は保証(成功 or 何もしないように実装)されているか?
- ジョブ中で(利用厳禁である)スレッドを使っていないか?
- ジョブのリトライメカニズムを(隠蔽・切り出しせず)ジョブ中に書いているか?
- バッチ処理にリトライは付いているか?
- トランザクションのコミットが完了したジョブのみキューに入れているか?
- ビジネスロジックをジョブ中に書いていないか?