20200923のRubyに関する記事は18件です。

Rails6 Couldn't find User with 'id' = sign_outとなりログアウトができない

エラー内容

スクリーンショット 2020-09-22 13.02.47.png

参考記事

https://qiita.com/chisaki0606/items/f1f03a6c226e49b5f7b3

ログアウトができない

今までできていたはずのログアウトが突如できなくなる事態が発生してしまう。このエラーで1日を費やしてしまったので、同じようなエラーとなっている人の助けになったらと思い、本稿を投稿することにした。

解決方法

参考記事の通りに

config/initializers/devise.rb
config.sign_out_via = :delete

を下記のように変更する。

config/initializers/devise.rb
config.sign_out_via = :get

しかし、このまま再びログアウトしてもエラーは解決しない。
というのも、devise.rbのコードは再起動しなければ反映されないため、そのままではエラーのままとなる。
そのため、

$ rails s 

として、localhost:3000に再びアクセスする必要がある。
そうしてあげると、エラーから脱出することができ、正常にログアウトすることができる。

終わりに

最後の再起動が重要であり、私はこれをしていないため、沼にハマることになってしまいました。同じようなエラーが悩んでいる方々の助けになれば幸いです。
また、記述に誤りがあるようでしたら、是非ご指摘いただけると幸いです。

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

Ruby on Railsでグラフを表示する方法(LazyHighChart)

まだまだプログラミングを学び始めたばかりなので拙い記事になってしまうかもしれませんが自分への備忘録&練習としてこういう記事を書いてきたいなと思います。よろしくおねがいします。

LazyHighChartとは

簡単に言うとグラフを表示するgemです。
HighChartsというJavaScript製グラフライブラリをRubyで使えるようにしたよって感じのgemみたい
早めに書いておきますがグラフ自体の表示方法を変えたりするのはまだよくわかっていないので今回は実装までしか書きません。

Gemの導入

gem 'lazy_high_charts'

まずはGemfileにこれを書いてあげてbundle install

これに加えてapplication.jsに

//= require highcharts/highcharts
//= require highcharts/highcharts-more
//= require highcharts/highstock

と加えてあげましょう。
これで準備はできました。

Viewでグラフを表示する

まずView側の記述です。

<%= high_chart('my-first-chart', @chart) %>

この記述をしたところにグラフが描画されます。

Controller側の設定

ここでグラフの種類を決めたり(棒グラフ、円グラフ、折れ線グラフなど)を決めていくわけですがまだ先述にもある通り細かいところがわかってないのでとりあえず私が実装させた記述を書きます。

  def create
    @post = Post.new(post_params)

    if @post.save
-----------------------------------------------------#グラフを表示するための記述
    @chart = LazyHighCharts::HighChart.new('graph') do |f|
      f.title(text: @post.select.name)
      f.xAxis(categories: ["Soldier", "Wizard", "Monk", "Fighter", "Gunner"])
      f.series(name: "", yAxis: 1, data: [@post.vs_soldier_id,@post.vs_wizard_id,@post.vs_monk_id,@post.vs_fighter_id,@post.vs_gunner_id])
      f.yAxis [
        {title: {text: "", margin: 0} },
        {title: {text: ""}, opposite: true},
      ]
      f.legend(align: 'none')
      f.chart({defaultSeriesType: "column"})
    end
-------------------------------------------------------------------------

    else
      render "new"
    end
  end

newアクションからストロングパラメーターで取得した値をcreateアクションで表示した時の記述です。

今回のは棒グラフが表示されます。
f.yAxis
f.legend
f.series
この3つは今回の実装でいらなかったんでよくわかってないです(すいません)

f.title
書いてあるとおり、ここに入れた値がグラフのタイトルになります。

f.xAxis
グラフの横軸の値です。今回はここは決まっていたんで文字列を入れてますね。

そしてf.seriesのなかにある
data: []
ここにデータを入れていきます。(ここに値を入れるために変数をあれこれしたんですが初心者にはここが辛かった…)

f.chart({defaultSeriesType: "column"})
ここの"column"がグラフの種類になるみたいですね。

以上です。

まだまだわかってないことがたくさんあってガバガバな記事なってしまいましたが少しずついいものが書けるよう頑張っていきたいです。では〜

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

Railsアプリケーションで投稿検索機能を実装する(whereメソッド)

アプリ: ラーメン屋の写真や情報を友達と共有できるSNS
Ruby: 2.6.5
Rails: 5.2.0

viewに検索フォームを設置する

form_withを使って、検索ワードをposts_controllerのsearchアクションに渡します。

app/views/layouts/_header.html.erb
・
・
・
<div class="post_search">
  <%= form_with url: search_posts_path, method: :get, local: true do |f| %>
    <%= f.text_field :search, class: 'form-control', placeholder: "キーワード検索"  %>
    <%= f.button :type => "submit" do %>
      <i class="fas fa-search"></i>
    <% end %>
  <% end %>
</div>
・
・
・

スクリーンショット 2020-09-23 18.54.03.jpg

ルーティング

routes.rb を下記のようにすることで、
/posts/searchというURL(search_posts_path)にgetリクエストを送ることでposts_controllerのsearchアクションにルーティングされます。

config/routes.rb
~
~
resources :posts, only: [:new, :create, :edit, :show, :update, :destroy] do
  get :search, on: :collection
end
~
~

posts_controller

検索ワードに合致する投稿を@postsで定義。

app/controllers/posts_controller.rb
~
~
def search
  @section_title = "「#{params[:search]}」の検索結果"
  @posts = if params[:search].present?
             Post.where(['shop_name LIKE ? OR nearest LIKE ?',
                        "%#{params[:search]}%", "%#{params[:search]}%"])
                 .paginate(page: params[:page], per_page: 12).recent
           else
             Post.none
           end
end
~
~

params[:search]でform_withから渡された検索ワードを受け取ります。
whereメソッドはテーブル内から条件に一致したレコードをすべて返します。
(検索ワードがない場合はPost.noneとし、投稿を取得しないようにしました。)

whereメソッド

whereメソッドでは下記のように

モデル名.where('カラム名 = ?', "値")

第一引数に ? を(プレースホルダーと呼ぶ)、第二引数に条件の値を入れることがあります。
このような書き方をすることで、SQLインジェクションというデータベースのデータを不正に操作する攻撃を防ぐことができるそうです。

whereの第二引数には下記のように

"%#{params[:search]}%"

検索ワードの前後に % を置いています。
こうすることで、「空白文字を含む任意の複数文字列」が検索ワードの前後に含まれても
その文字列を持つレコードを返すことができます。

%二郎%
→ 「二郎」「ラーメン二郎」「つけ麺二郎」「ラーメン二郎八王子店」「二郎歌舞伎町店」
 どれも該当する。

%二郎
→ 「二郎」「ラーメン二郎」「つけ麺二郎」は該当するが、
 「ラーメン二郎八王子店」「二郎歌舞伎町店」は該当しない。

下記のように書くことでshop_nameとnearestという2つのカラムから
検索ワードに該当する文字列を検索し、レコードを返します。

Post.where(['shop_name LIKE ? OR nearest LIKE ?',
            "%#{params[:search]}%", "%#{params[:search]}%"])

あとは@postsをapp/views/posts/search.html.erbで表示させるようにしてください。

こんな感じ

スクリーンショット 2020-09-23 21.33.34.jpg

スクリーンショット 2020-09-23 21.34.05.jpg

参考

【Rails】1つの検索フォームで複数カラムをまたいで検索する方法
【Rails】whereメソッドを使って欲しいデータの取得をしよう!

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

[初心者]Rails6 Vue Postgres開発環境をDocker-Composeを使って構築する方法

はじめに

Dockerを用いてRails,Vue,Posgre環境下で開発したいなと思った時に、
Rails,Vueの環境作りに手こずりましたので自分のメモとして保存します。

この記事で分かること

・Rails6とVueとPostgresのDocker環境が構築できる
・RailsとVueの連動ができる
・PryにてインスタンスとDBの操作ができる

環境

MacOS Mojave
Ruby 2.6.4
Rails 6.0.3.3
Vue 2.6.12
Webpack 4.44.2
yarn 1.22.5
Docker 2.3.0.5
VScode

Githubのリンク

僕のGithubのDocker-Start-Kitです。よろしければご活用ください。
Whiro0501/Docker_Rails6_Vue

Docker環境をMacに構築

ターミナルを開く
mkdirで任意の名前のディレクトリを作成
cdで作成したディレクトリに移動
code.でVScodeを開く

terminal
mkdir qiita
cd qiita
code .

VScodeが起動
VScode上のターミナルで作業していくので
ターミナルが起動していなければ⬆︎+Control+@でターミナルを開く

VScodeのターミナル上で以下コマンドを入力してリモートリポジトリのファイルをローカルにコピー

VScode
git clone https://github.com/Whiro0501/Docker_Rails6_Vue.git

cdでDocker_Rails6_Vue/ディレクトリに移動

VScode
cd Docker_Rails6_Vue/

以下コマンドを実行してDockerイメージをビルド

VScode
docker-compose build

以下を実行して、必要なノードモジュールを取得

VScode
docker-compose run web yarn install --check-files

以下を実行して DB(Postgres)を作成

VScode
docker-compose run web rake db:create

別のターミナルを開く
以下を実行してレールズアプリケーションを起動

VScode
#別ターミナルが億劫なら、docker-compose up -dでも良い 
docker-compose up

さらに別のターミナルを開く
Viewを更新するたび毎回コンパイルが発生し、時間がかかるため以下を実行

VScode
docker-compose exec web ./bin/webpack-dev-server

Webブラウザで以下にアクセス

Webブラウザ
http://localhost:3000

Railsアプリケーションが起動することを確認

スクリーンショット 2020-09-23 20.42.47.png

VueとRailsの連動ってどうやるの?

初期構築時点ではRailsとVueが連動されておりませんので連動させていく。

以下でhomeコントローラを作成する(コントローラー名はなんでも良い)。

VScode
docker-compose exec web rails g controller home index

index.html.erbが作成

index.html.erb
<h1>Home#index</h1>
<p>Find me in app/views/home/index.html.erb</p>

以下ファイルに"root to: 'home#index'"を追加

routes.rb
Rails.application.routes.draw do
  root to:  'home#index'
  # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
end

Webブラウザで以下にアクセス

Webブラウザ
http://localhost:3000

まずはindex.html.erbに記述されている内容が表示

スクリーンショット 2020-09-23 21.07.31.png

Rails側で下地ができたので、次にVueとの連動の設定
app.vueの初期設定

app.vue
<template>
  <div id="app">
    <p>{{ message }}</p>
  </div>
</template>

<script>
export default {
  data: function () {
    return {
      message: "Hello Vue!"
    }
  }
}
</script>

<style scoped>
p {
  font-size: 2em;
  text-align: center;
}
</style>

hello_vue.jsの初期設定

hello_vue.js
import Vue from 'vue'
import App from '../app.vue'

document.addEventListener('DOMContentLoaded', () => {
  const app = new Vue({
    render: h => h(App)
  }).$mount()
  document.body.appendChild(app.$el)

  console.log(app)
})

app.vueが単一ファイルコンポーネントであり、
hello_vue.jsにオブジェクトとして渡される。
それをindex.html.erbに表示させるよう設定する。

index.html.erbを以下の通り設定

index.html.erb
<h1>Home#index</h1>
<p>Find me in app/views/home/index.html.erb</p>

<%= javascript_pack_tag 'hello_vue.js' %>
<%= stylesheet_pack_tag 'hello_vue.js' %>

Webブラウザで以下にアクセス

Webブラウザ
http://localhost:3000

Vueとの連動が完了!!

スクリーンショット 2020-09-23 21.23.06.png

Githubののファイルの説明

Whiro0501/Docker_Rails6_Vue
まず上記リンクのGithubの状態から説明する
端的に説明すると以下2つを設定した後の状態となる
従ってRails6とVueとPostgresが使用できる環境を整えることができるという理屈である。

VScode
docker-compose run web rails new . --force --database=postgresql --webpack=vue

加えて上記の状態だとPostgresがうまくRailsと連動できないため以下のように設定する。
こちらも公式ドキュメントを参考とした。

database.yml
default: &default
  adapter: postgresql
  encoding: unicode
  host: db
  username: postgres
  password: password
  pool: 5

development:
  <<: *default
  database: myapp_development


test:
  <<: *default
  database: myapp_test


production:
  <<: *default
  database: myapp_production
  username: myapp
  password: <%= ENV['MYAPP_DATABASE_PASSWORD'] %>

Dockerfile

公式ドキュメントのベストプラクティスを参照にした。
DockerはFROM, RUN, COPY毎にレイヤーが作成されるため
RUNやCOPYはできるだけまとめると少ないレイヤーで収めることができるとのこと。

Dockerfile
FROM ruby:2.6

# `apt-get install yarn`とするとエラーになる
# プロジェクトに必要なツールをインストール
# &&で繋げてコマンドを実行することによりレイヤーを1つとする
#apt-get update と apt-get installは同一RUN上で行う(分けると最新版を使用できない)
#RUNはイメージの作成次に実行(CMDはコンテナ起動時に実行)
RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \
    echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \
    apt-get update -qq && apt-get install -y nodejs postgresql-client vim && \
    apt-get install -y yarn

# ディレクトリの作成
RUN mkdir /myapp

# 作業ディレクトリの指定
#RUN ,  COPY,  ADD 命令のみレイヤを作成するためWORKDIRは気にしなくて良い
# 絶対パスとする
WORKDIR /myapp

# Gemfileが更新された時のみ、レイヤを再構築
#先にプロジェクト全体をコピーしないのはレイヤーを分けるため
COPY Gemfile /myapp/Gemfile
COPY Gemfile.lock /myapp/Gemfile.lock
# ライブラリの依存関係をインストール
RUN bundle install

# プロジェクト全体をコピー(Gemfile/Gemfile.lockはコピーされない)
COPY . /myapp

#コンテナを起動する毎に実行されるスクリプトを追加
COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]

#コンテナの公開ポート番号の指定
EXPOSE 3000

#指定しなければコンテナ起動時にデフォルトで実行する処理
#Dockerfile では CMD 命令を 1 つしか記述できない
#ENTRYPOINT 命令に対するデフォルト引数としてCMDを使用可能
CMD ["rails", "server", "-b", "0.0.0.0"]

docker-compose

docker-compose.ymlもDocker公式ドキュメントを参考にした。

docker-compose.yml
version: '3'
services:
    db:
        # DBにpostgresを使用
        image: postgres
        # ホストの./tmp/dbと/var/lib/postgresql/dataを同期させる
        volumes:
            - ./tmp/db:/var/lib/postgresql/data
        # 環境変数の指定
        environment:
            POSTGRES_PASSWORD: password
    web:
        build: .
        # コンテナ起動時にserver.pidを削除し、rails sを実行する
        command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
        # ホストのカレントディレクトリをコンテナの/myappと同期させる
        volumes:
            - .:/myapp
        # ホストとコンテナ間をポートフォワードする
        ports:
            - '3000:3000'
        # サービス間の依存関係
        depends_on:
            - db
        # Docker環境でByebugを使用
        stdin_open: true
        tty: true

Gemfile

Gemfileに関してはデフォルトで入っているもの主となる。
追加したパッケージはコメントしているため不要であれば削除して構わない

Gemfile
source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }


ruby '2.6.6'

# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '~> 6.0.3', '>= 6.0.3.3'
# Use postgresql as the database for Active Record
gem 'pg', '>= 0.18', '< 2.0'
# Use Puma as the app server
gem 'puma', '~> 4.1'
# Use SCSS for stylesheets
gem 'sass-rails', '>= 6'
# Transpile app-like JavaScript. Read more: https://github.com/rails/webpacker
gem 'webpacker', '~> 4.0'
# Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks
gem 'turbolinks', '~> 5'
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
gem 'jbuilder', '~> 2.7'
# Use Redis adapter to run Action Cable in production
# gem 'redis', '~> 4.0'
# Use Active Model has_secure_password
# gem 'bcrypt', '~> 3.1.7'

# Use Active Storage variant
# gem 'image_processing', '~> 1.2'

# Reduces boot times through caching; required in config/boot.rb
gem 'bootsnap', '>= 1.4.2', require: false

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'
end

group :development do
  # Access an interactive console on exception pages or by calling 'console' anywhere in the code.
  gem 'web-console', '>= 3.3.0'
  gem 'listen', '~> 3.2'
  # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
  gem 'spring'
  gem 'spring-watcher-listen', '~> 2.0.0'
  #自動補完用ツールの導入
  gem 'solargraph'
  #静的コード解析ツールの導入
  gem 'rubocop'
  gem 'rubocop-rails'
  #erbのフォーマットツールの導入
  gem 'htmlbeautifier'
end

group :test do
  # Adds support for Capybara system testing and selenium driver
  gem 'capybara', '>= 2.15'
  gem 'selenium-webdriver'
  # Easy installation and use of web drivers to run system tests with browsers
  gem 'webdrivers'
end

# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]

package.json

package.jsonについてはvue-routerとvuexを使いたいため追加している
Vuetify等、UIフレームワークを使用したいようであれば追加する。

package.json
{
    "name": "myapp",
    "private": true,
    "dependencies": {
        "@rails/actioncable": "^6.0.0",
        "@rails/activestorage": "^6.0.0",
        "@rails/ujs": "^6.0.0",
        "@rails/webpacker": "4.3.0",
        "turbolinks": "^5.2.0",
        "vue": "^2.6.12",
        "vue-loader": "^15.9.3",
        "vue-template-compiler": "^2.6.12",
        "vue-router": "^3.0.1",
        "vuex": "^3.0.1"
    },
    "version": "0.1.0",
    "devDependencies": {
        "webpack-dev-server": "^3.11.0"
    }
}

Railsでデバッグする

VScode上のターミナルを開く
以下をターミナルで実行

VScode
docker-compose exec web rails console

pry が起動

VScode
[1] pry(main)> 

postコントローラーを作成

docker-compose exec web rails g controller post index

post_controller.rbを修正

post_controller.rb
class PostController < ApplicationController
  def index
    @post = Post.all
  end
end

postのモデルを作成

VScode
docker-compose exec web rails g model post name:string age:integer

DBにモデルを反映させる

VScode
docker-compose exec web rails db:migrate

index.html.erbを以下に書き換え

index.html.erb
<%= @post.name %>
<%= @post.age %>

routes.rbを以下に修正

routes.rb
Rails.application.routes.draw do
  root to:  'post#index'
  # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
end

pryでPostモデルのインスタンスを作成

VScode
@post = Post.new
=> #<Post:0x00005589bc4beb78 id: nil, name: nil, age: nil, created_at: nil, updated_at: nil>

#まだnameやageにはデータを入れていない
#DBに保存もされていない
Post.all
=>   Post Load (2.2ms)  SELECT "posts".* FROM "posts"
[]

#DBにインスタンスを保存する
@post.save
   (0.7ms)  BEGIN
  Post Create (5.9ms)  INSERT INTO "posts" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "id"  [["created_at", "2020-09-23 13:06:47.962085"], ["updated_at", "2020-09-23 13:06:47.962085"]]
   (3.1ms)  COMMIT
=> true

#もう一度Post.allをしてデータをDBから取得すると保存したインスタンスが呼び出される
Post.all
=>   Post Load (2.2ms)  SELECT "posts".* FROM "posts"
[#<Post:0x00005589bceec5f0
  id: 1,
  name: nil,
  age: nil,
  created_at: Wed, 23 Sep 2020 13:06:47 UTC +00:00,
  updated_at: Wed, 23 Sep 2020 13:06:47 UTC +00:00>]

#@postインスタンスにデータを入れてみる
@post.name = "Hiro"
=> "Hiro"

@post.age = "29"
=> "29"

#再び保存
@post.save
Post Update (4.0ms)  UPDATE "posts" SET "name" = $1, "updated_at" = $2 WHERE "posts"."id" = $3  [["name", "Hiro"], ["updated_at", "2020-09-23 13:10:26.486888"], ["id", 1]]
Post Update (2.0ms)  UPDATE "posts" SET "age" = $1, "updated_at" = $2 WHERE "posts"."id" = $3  [["age", 29], ["updated_at", "2020-09-23 13:10:56.785029"], ["id", 1]]
   (1.0ms)  COMMIT
=> true

#データが保存されている
Post.all
=>   Post Load (1.4ms)  SELECT "posts".* FROM "posts"
[#<Post:0x00007f1ddc7a77a8
  id: 1,
  name: "Hiro",
  age: 29,
  created_at: Wed, 23 Sep 2020 13:06:47 UTC +00:00,
  updated_at: Wed, 23 Sep 2020 13:10:56 UTC +00:00>]

Webブラウザで以下にアクセス

Webブラウザ
http://localhost:3000

以下のようにDBからデータを取得できればOK

スクリーンショット 2020-09-23 22.27.01.png

Binding.pryを試してみる

post_controller.rbを修正

class PostController < ApplicationController
  def index
    binding.pry
    @post = Post.all
  end
end

Webブラウザで以下にアクセス

Webブラウザ
http://localhost:3000

以下の画面でフリーズする(正しい挙動)
スクリーンショット 2020-09-23 22.30.19.png

pryのターミナルに戻る

pry(main)> 

以上で、pryでデバッグする環境が整った

参考

以下のサイトを参考にさせていただきました。
Docker ドキュメント日本語化プロジェクト
クィックスタート: Compose と Rails
Dockerfile のベストプラクティス
Webpacker の基本的な仕組み
Dockerを使って「Rails / PostgreSQL」の開発環境を作ろう!

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

Rails6 Vue Postgres開発環境をDocker-Composeを使って構築する方法

はじめに

Dockerを用いてRails,Vue,Posgre環境下で開発したいなと思った時に、
Rails,Vueの環境作りに手こずりましたので自分のメモとして保存します。

この記事で分かること

・Rails6とVueとPostgresのDocker環境が構築できる
・RailsとVueの連動ができる
・PryにてインスタンスとDBの操作ができる

環境

MacOS Mojave
Ruby 2.6.4
Rails 6.0.3.3
Vue 2.6.12
Webpack 4.44.2
yarn 1.22.5
Docker 2.3.0.5
VScode

Githubのリンク

僕のGithubのDocker-Start-Kitです。よろしければご活用ください。
Whiro0501/Docker_Rails6_Vue

Docker環境をMacに構築

ターミナルを開く
mkdirで任意の名前のディレクトリを作成
cdで作成したディレクトリに移動
code.でVScodeを開く

terminal
mkdir qiita
cd qiita
code .

VScodeが起動
VScode上のターミナルで作業していくので
ターミナルが起動していなければ⬆︎+Control+@でターミナルを開く

VScodeのターミナル上で以下コマンドを入力してリモートリポジトリのファイルをローカルにコピー

VScode
git clone https://github.com/Whiro0501/Docker_Rails6_Vue.git

cdでDocker_Rails6_Vue/ディレクトリに移動

VScode
cd Docker_Rails6_Vue/

以下コマンドを実行してDockerイメージをビルド

VScode
docker-compose build

以下を実行して、必要なノードモジュールを取得

VScode
docker-compose run web yarn install --check-files

以下を実行して DB(Postgres)を作成

VScode
docker-compose run web rake db:create

別のターミナルを開く
以下を実行してレールズアプリケーションを起動

VScode
#別ターミナルが億劫なら、docker-compose up -dでも良い 
docker-compose up

さらに別のターミナルを開く
Viewを更新するたび毎回コンパイルが発生し、時間がかかるため以下を実行

VScode
docker-compose exec web ./bin/webpack-dev-server

Webブラウザで以下にアクセス

Webブラウザ
http://localhost:3000

Railsアプリケーションが起動することを確認

スクリーンショット 2020-09-23 20.42.47.png

VueとRailsの連動ってどうやるの?

初期構築時点ではRailsとVueが連動されておりませんので連動させていく。

以下でhomeコントローラを作成する(コントローラー名はなんでも良い)。

VScode
docker-compose exec web rails g controller home index

index.html.erbが作成

index.html.erb
<h1>Home#index</h1>
<p>Find me in app/views/home/index.html.erb</p>

以下ファイルに"root to: 'home#index'"を追加

routes.rb
Rails.application.routes.draw do
  root to:  'home#index'
  # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
end

Webブラウザで以下にアクセス

Webブラウザ
http://localhost:3000

まずはindex.html.erbに記述されている内容が表示

スクリーンショット 2020-09-23 21.07.31.png

Rails側で下地ができたので、次にVueとの連動の設定
app.vueの初期設定

app.vue
<template>
  <div id="app">
    <p>{{ message }}</p>
  </div>
</template>

<script>
export default {
  data: function () {
    return {
      message: "Hello Vue!"
    }
  }
}
</script>

<style scoped>
p {
  font-size: 2em;
  text-align: center;
}
</style>

hello_vue.jsの初期設定

hello_vue.js
import Vue from 'vue'
import App from '../app.vue'

document.addEventListener('DOMContentLoaded', () => {
  const app = new Vue({
    render: h => h(App)
  }).$mount()
  document.body.appendChild(app.$el)

  console.log(app)
})

app.vueが単一ファイルコンポーネントであり、
hello_vue.jsにオブジェクトとして渡される。
それをindex.html.erbに表示させるよう設定する。

index.html.erbを以下の通り設定

index.html.erb
<h1>Home#index</h1>
<p>Find me in app/views/home/index.html.erb</p>

<%= javascript_pack_tag 'hello_vue.js' %>
<%= stylesheet_pack_tag 'hello_vue.js' %>

Webブラウザで以下にアクセス

Webブラウザ
http://localhost:3000

Vueとの連動が完了!!

スクリーンショット 2020-09-23 21.23.06.png

Githubののファイルの説明

Whiro0501/Docker_Rails6_Vue
まず上記リンクのGithubの状態から説明する
端的に説明すると以下2つを設定した後の状態となる
従ってRails6とVueとPostgresが使用できる環境を整えることができるという理屈である。

VScode
docker-compose run web rails new . --force --database=postgresql --webpack=vue

加えて上記の状態だとPostgresがうまくRailsと連動できないため以下のように設定する。
こちらも公式ドキュメントを参考とした。

database.yml
default: &default
  adapter: postgresql
  encoding: unicode
  host: db
  username: postgres
  password: password
  pool: 5

development:
  <<: *default
  database: myapp_development


test:
  <<: *default
  database: myapp_test


production:
  <<: *default
  database: myapp_production
  username: myapp
  password: <%= ENV['MYAPP_DATABASE_PASSWORD'] %>

Dockerfile

公式ドキュメントのベストプラクティスを参照にした。
DockerはFROM, RUN, COPY毎にレイヤーが作成されるため
RUNやCOPYはできるだけまとめると少ないレイヤーで収めることができるとのこと。

Dockerfile
FROM ruby:2.6

# `apt-get install yarn`とするとエラーになる
# プロジェクトに必要なツールをインストール
# &&で繋げてコマンドを実行することによりレイヤーを1つとする
#apt-get update と apt-get installは同一RUN上で行う(分けると最新版を使用できない)
#RUNはイメージの作成次に実行(CMDはコンテナ起動時に実行)
RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \
    echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \
    apt-get update -qq && apt-get install -y nodejs postgresql-client vim && \
    apt-get install -y yarn

# ディレクトリの作成
RUN mkdir /myapp

# 作業ディレクトリの指定
#RUN ,  COPY,  ADD 命令のみレイヤを作成するためWORKDIRは気にしなくて良い
# 絶対パスとする
WORKDIR /myapp

# Gemfileが更新された時のみ、レイヤを再構築
#先にプロジェクト全体をコピーしないのはレイヤーを分けるため
COPY Gemfile /myapp/Gemfile
COPY Gemfile.lock /myapp/Gemfile.lock
# ライブラリの依存関係をインストール
RUN bundle install

# プロジェクト全体をコピー(Gemfile/Gemfile.lockはコピーされない)
COPY . /myapp

#コンテナを起動する毎に実行されるスクリプトを追加
COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]

#コンテナの公開ポート番号の指定
EXPOSE 3000

#指定しなければコンテナ起動時にデフォルトで実行する処理
#Dockerfile では CMD 命令を 1 つしか記述できない
#ENTRYPOINT 命令に対するデフォルト引数としてCMDを使用可能
CMD ["rails", "server", "-b", "0.0.0.0"]

docker-compose

docker-compose.ymlもDocker公式ドキュメントを参考にした。

docker-compose.yml
version: '3'
services:
    db:
        # DBにpostgresを使用
        image: postgres
        # ホストの./tmp/dbと/var/lib/postgresql/dataを同期させる
        volumes:
            - ./tmp/db:/var/lib/postgresql/data
        # 環境変数の指定
        environment:
            POSTGRES_PASSWORD: password
    web:
        build: .
        # コンテナ起動時にserver.pidを削除し、rails sを実行する
        command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
        # ホストのカレントディレクトリをコンテナの/myappと同期させる
        volumes:
            - .:/myapp
        # ホストとコンテナ間をポートフォワードする
        ports:
            - '3000:3000'
        # サービス間の依存関係
        depends_on:
            - db
        # Docker環境でByebugを使用
        stdin_open: true
        tty: true

Gemfile

Gemfileに関してはデフォルトで入っているもの主となる。
追加したパッケージはコメントしているため不要であれば削除して構わない

Gemfile
source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }


ruby '2.6.6'

# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '~> 6.0.3', '>= 6.0.3.3'
# Use postgresql as the database for Active Record
gem 'pg', '>= 0.18', '< 2.0'
# Use Puma as the app server
gem 'puma', '~> 4.1'
# Use SCSS for stylesheets
gem 'sass-rails', '>= 6'
# Transpile app-like JavaScript. Read more: https://github.com/rails/webpacker
gem 'webpacker', '~> 4.0'
# Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks
gem 'turbolinks', '~> 5'
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
gem 'jbuilder', '~> 2.7'
# Use Redis adapter to run Action Cable in production
# gem 'redis', '~> 4.0'
# Use Active Model has_secure_password
# gem 'bcrypt', '~> 3.1.7'

# Use Active Storage variant
# gem 'image_processing', '~> 1.2'

# Reduces boot times through caching; required in config/boot.rb
gem 'bootsnap', '>= 1.4.2', require: false

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'
end

group :development do
  # Access an interactive console on exception pages or by calling 'console' anywhere in the code.
  gem 'web-console', '>= 3.3.0'
  gem 'listen', '~> 3.2'
  # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
  gem 'spring'
  gem 'spring-watcher-listen', '~> 2.0.0'
  #自動補完用ツールの導入
  gem 'solargraph'
  #静的コード解析ツールの導入
  gem 'rubocop'
  gem 'rubocop-rails'
  #erbのフォーマットツールの導入
  gem 'htmlbeautifier'
end

group :test do
  # Adds support for Capybara system testing and selenium driver
  gem 'capybara', '>= 2.15'
  gem 'selenium-webdriver'
  # Easy installation and use of web drivers to run system tests with browsers
  gem 'webdrivers'
end

# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]

package.json

package.jsonについてはvue-routerとvuexを使いたいため追加している
Vuetify等、UIフレームワークを使用したいようであれば追加する。

package.json
{
    "name": "myapp",
    "private": true,
    "dependencies": {
        "@rails/actioncable": "^6.0.0",
        "@rails/activestorage": "^6.0.0",
        "@rails/ujs": "^6.0.0",
        "@rails/webpacker": "4.3.0",
        "turbolinks": "^5.2.0",
        "vue": "^2.6.12",
        "vue-loader": "^15.9.3",
        "vue-template-compiler": "^2.6.12",
        "vue-router": "^3.0.1",
        "vuex": "^3.0.1"
    },
    "version": "0.1.0",
    "devDependencies": {
        "webpack-dev-server": "^3.11.0"
    }
}

Railsでデバッグする

VScode上のターミナルを開く
以下をターミナルで実行

VScode
docker-compose exec web rails console

pry が起動

VScode
[1] pry(main)> 

postコントローラーを作成

docker-compose exec web rails g controller post index

post_controller.rbを修正

post_controller.rb
class PostController < ApplicationController
  def index
    @post = Post.all
  end
end

postのモデルを作成

VScode
docker-compose exec web rails g model post name:string age:integer

DBにモデルを反映させる

VScode
docker-compose exec web rails db:migrate

index.html.erbを以下に書き換え

index.html.erb
<%= @post.name %>
<%= @post.age %>

routes.rbを以下に修正

routes.rb
Rails.application.routes.draw do
  root to:  'post#index'
  # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
end

pryでPostモデルのインスタンスを作成

VScode
@post = Post.new
=> #<Post:0x00005589bc4beb78 id: nil, name: nil, age: nil, created_at: nil, updated_at: nil>

#まだnameやageにはデータを入れていない
#DBに保存もされていない
Post.all
=>   Post Load (2.2ms)  SELECT "posts".* FROM "posts"
[]

#DBにインスタンスを保存する
@post.save
   (0.7ms)  BEGIN
  Post Create (5.9ms)  INSERT INTO "posts" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "id"  [["created_at", "2020-09-23 13:06:47.962085"], ["updated_at", "2020-09-23 13:06:47.962085"]]
   (3.1ms)  COMMIT
=> true

#もう一度Post.allをしてデータをDBから取得すると保存したインスタンスが呼び出される
Post.all
=>   Post Load (2.2ms)  SELECT "posts".* FROM "posts"
[#<Post:0x00005589bceec5f0
  id: 1,
  name: nil,
  age: nil,
  created_at: Wed, 23 Sep 2020 13:06:47 UTC +00:00,
  updated_at: Wed, 23 Sep 2020 13:06:47 UTC +00:00>]

#@postインスタンスにデータを入れてみる
@post.name = "Hiro"
=> "Hiro"

@post.age = "29"
=> "29"

#再び保存
@post.save
Post Update (4.0ms)  UPDATE "posts" SET "name" = $1, "updated_at" = $2 WHERE "posts"."id" = $3  [["name", "Hiro"], ["updated_at", "2020-09-23 13:10:26.486888"], ["id", 1]]
Post Update (2.0ms)  UPDATE "posts" SET "age" = $1, "updated_at" = $2 WHERE "posts"."id" = $3  [["age", 29], ["updated_at", "2020-09-23 13:10:56.785029"], ["id", 1]]
   (1.0ms)  COMMIT
=> true

#データが保存されている
Post.all
=>   Post Load (1.4ms)  SELECT "posts".* FROM "posts"
[#<Post:0x00007f1ddc7a77a8
  id: 1,
  name: "Hiro",
  age: 29,
  created_at: Wed, 23 Sep 2020 13:06:47 UTC +00:00,
  updated_at: Wed, 23 Sep 2020 13:10:56 UTC +00:00>]

Webブラウザで以下にアクセス

Webブラウザ
http://localhost:3000

以下のようにDBからデータを取得できればOK

スクリーンショット 2020-09-23 22.27.01.png

Binding.pryを試してみる

post_controller.rbを修正

class PostController < ApplicationController
  def index
    binding.pry
    @post = Post.all
  end
end

Webブラウザで以下にアクセス

Webブラウザ
http://localhost:3000

以下の画面でフリーズする(正しい挙動)
スクリーンショット 2020-09-23 22.30.19.png

pryのターミナルに戻る

pry(main)> 

以上で、pryでデバッグする環境が整った

参考

以下のサイトを参考にさせていただきました。
Docker ドキュメント日本語化プロジェクト
クィックスタート: Compose と Rails
Dockerfile のベストプラクティス
Webpacker の基本的な仕組み
Dockerを使って「Rails / PostgreSQL」の開発環境を作ろう!

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

Better_errors導入後、エラー画面が変わらない

better_error入れて見たがエラー画面変わらず

エラー画面をリッチに使いやすくするruby gem "Better_errors"。
リポジトリ見ると、railsであれば素直にbundle installすれば使えるよと書いてあるが
エラー画面は変わらず…

解決策

以下を追記して解決しました。

/config/environments/development.rb
BetterErrors::Middleware.allow_ip! "0.0.0.0/0"

▼参考:Dockerコンテナでbetter_errorが働かない
https://github.com/BetterErrors/better_errors/issues/270
▼gitのソースはおそらくここ
https://github.com/BetterErrors/better_errors/wiki/Allowing-access-to-the-console

DockerやVagrantを利用して開発する場合は
railsがbetter_errorsを許可するか判断するので
設定が必要になるそうです。

gitの質疑だとDockerのIPアドレス入れても上手く行かない!と言っているので
じゃあもう0.0.0.0/0にして全てのIPアドレスを許可いた状態にしよう!という話しをしています。

localhostのIPアドレスでもOKなので

/config/environments/development.rb
BetterErrors::Middleware.allow_ip! "127.0.0.1/0"

これでもいけました。

因みに 127.0.0.1 とは

ローカル・ループバック・アドレス。
自分自身を表す特別なIPアドレス。localhostのIPアドレスと言ったらこれにあたる。
▼参考
https://www.atmarkit.co.jp/ait/articles/0610/14/news021.html

です。

ご参考までに。

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

Railsでログイン機能をシンプルに名前とパスワードだけで実装する(1)

はじめに

rails tutorialで使われているE-mailとパスワードを使ったログインより、もっとシンプルに実装する。
deviseなどのgemは使わない。

前提

  • rails tutorialを終了した程度の基本的なrailsの知識
  • Mac
  • Ruby on Rails6
  • テストは省略

目標

ここではRoomテーブルの名前とパスワードを使ってログインする。

手順

  • まずログインするモデルを作成する。今回はRoomテーブル
MacBook % rails g model Room name:string
  • 生成されたマイグレーションファイルに追記し、マイグレーションをかける
  • これによって、空の名前はデータベースに保存できなくなる
db/migrate/タイムスタンプ.create_rooms.rb
class CreateRooms < ActiveRecord::Migration[6.0]
  def change
    create_table :rooms do |t|
      t.string :name, null: false

      t.timestamps
    end
  end
end
MacBook % rails db:migrate
  • Room.rbにhas_secure_passwordのメソッドを追加する
app/models/room.rb
class Room < ApplicationRecord
  validates :name, presence: true, length: { maximum: 50 }
  has_secure_password
end
  • password_digestカラムを追加するマイグレーションを作成し、マイグレーションをかける
MacBook % rails generate migration add_password_digest_to_rooms password_digest:string
MacBook % rails db:migrate
  • Gemfileを開き、gem 'bcrypt'のコメントアウトを解除しinstallする
Gemfile
~
# gem 'redis', '~> 4.0'
# Use Active Model has_secure_password
gem 'bcrypt', '~> 3.1.7'

# Use Active Storage variant
# gem 'image_processing', '~> 1.2'
~
  • 空のpasswordと最低文字数を追加で制限する
app/models/room.rb
class Room < ApplicationRecord
  validates :name, presence: true, length: { maximum: 50 }
  has_secure_password
  validates :password, presence: true, length: { minimum: 6 }
end
  • Rails console でインスタンスが生成、データベースに保存できるか確認する
サーバーを立ち上げているのとは別のターミナル
MacBook % rails c  
~
irb(main):001:0> @room = Room.new(name:"tokyoroom",password:"password",password_confirmation:"password")
   (2.2ms)  SET NAMES utf8mb4,  @@SESSION.sql_mode = CONCAT(CONCAT(@@sql_mode, ',STRICT_ALL_TABLES'), ',NO_AUTO_VALUE_ON_ZERO'),  @@SESSION.sql_auto_is_null = 0, @@SESSION.wait_timeout = 2147483
=> #<Room id: nil, name: "tokyoroom", created_at: nil, updated_at: nil, password_digest: [FILTERED]>
irb(main):002:0> @room.save
   (0.7ms)  BEGIN
  Room Create (3.3ms)  INSERT INTO `rooms` (`name`, `created_at`, `updated_at`, `password_digest`) VALUES ('tokyoroom', '2020-09-23 07:26:30.787094', '2020-09-23 07:26:30.787094', '$2a$12$SmCtpmRZn0M5BMtuT6BmAOKO.DMAmMOIYSXoHlxDrSbA2AQcAYnMC')
   (5.0ms)  COMMIT
=> true

続き[2]

(https://qiita.com/yongjugithub/items/65fcdf73e42857297321)

参考文献

https://railstutorial.jp/chapters/modeling_users?version=5.1#sec-adding_a_secure_password

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

Railsでログイン機能をシンプルに名前とパスワードだけで実装する

はじめに

rails tutorialで使われているE-mailとパスワードを使ったログインより、もっとシンプルに実装する。
deviseなどのgemは使わない。

前提

  • rails tutorialを終了した程度の基本的なrailsの知識
  • Mac
  • Ruby on Rails6
  • テストは省略

目標

ここではRoomテーブルの名前とパスワードを使ってログインする。

手順

  • まずログインするモデルを作成する。今回はRoomテーブル
MacBook % rails g model Room name:string
  • 生成されたマイグレーションファイルに追記し、マイグレーションをかける
  • これによって、空の名前はデータベースに保存できなくなる
db/migrate/タイムスタンプ.create_rooms.rb
class CreateRooms < ActiveRecord::Migration[6.0]
  def change
    create_table :rooms do |t|
      t.string :name, null: false

      t.timestamps
    end
  end
end
MacBook % rails db:migrate
  • Room.rbにhas_secure_passwordのメソッドを追加する
app/models/room.rb
class Room < ApplicationRecord
  validates :name, presence: true, length: { maximum: 50 }
  has_secure_password
end
  • password_digestカラムを追加するマイグレーションを作成し、マイグレーションをかける
MacBook % rails generate migration add_password_digest_to_rooms password_digest:string
MacBook % rails db:migrate
  • Gemfileを開き、gem 'bcrypt'のコメントアウトを解除しinstallする
Gemfile
~
# gem 'redis', '~> 4.0'
# Use Active Model has_secure_password
gem 'bcrypt', '~> 3.1.7'

# Use Active Storage variant
# gem 'image_processing', '~> 1.2'
~
  • 空のpasswordと最低文字数を追加で制限する
app/models/room.rb
class Room < ApplicationRecord
  validates :name, presence: true, length: { maximum: 50 }
  has_secure_password
  validates :password, presence: true, length: { minimum: 6 }
end
  • Rails console でインスタンスが生成、データベースに保存できるか確認する
サーバーを立ち上げているのとは別のターミナル
MacBook % rails c  
~
irb(main):001:0> @room = Room.new(name:"tokyoroom",password:"password",password_confirmation:"password")
   (2.2ms)  SET NAMES utf8mb4,  @@SESSION.sql_mode = CONCAT(CONCAT(@@sql_mode, ',STRICT_ALL_TABLES'), ',NO_AUTO_VALUE_ON_ZERO'),  @@SESSION.sql_auto_is_null = 0, @@SESSION.wait_timeout = 2147483
=> #<Room id: nil, name: "tokyoroom", created_at: nil, updated_at: nil, password_digest: [FILTERED]>
irb(main):002:0> @room.save
   (0.7ms)  BEGIN
  Room Create (3.3ms)  INSERT INTO `rooms` (`name`, `created_at`, `updated_at`, `password_digest`) VALUES ('tokyoroom', '2020-09-23 07:26:30.787094', '2020-09-23 07:26:30.787094', '$2a$12$SmCtpmRZn0M5BMtuT6BmAOKO.DMAmMOIYSXoHlxDrSbA2AQcAYnMC')
   (5.0ms)  COMMIT
=> true

参考文献

https://railstutorial.jp/chapters/modeling_users?version=5.1#sec-adding_a_secure_password

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

select_tag 選択値設定(include_blank => true)

select_tag 空白行設定(include_blank => true)時、
DBから検索された値をVALに設定しても、空白テキストが表示されている
対策:$("#selectbox").val(DBから検索値).trigger('change')

include_blank => falseなら、
$("#selectbox").val(DBから検索値)

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

Rails Kaminariでページネーション機能を作る

バージョン

・ruby 2.6.6
・rails 5.2.4.1

gem 'kaminari' を実装

 投稿機能を持ったアプリを作った際には、ページネーション機能はUX的にも必須です。
そこで、gemのKaminariを使って、ページネーション機能の実装を行います。

まずGemfileにkaminariを追加して、bundle installを実行

Gemfile
gem 'kaminari', '~> 1.2.0'
ターミナル
bundle install

Controllerを修正

 すでに投稿機能を作成し、投稿一覧ページがあるのであれば、Controlerを修正する必要があります。
 元々の自分の投稿一覧ページにおけるControllerは以下の通りです。

controllers/posts_controller.rb
def index
 @posts = Post.all
end

これらを次のように変更する。

controllers/posts_controller.rb
def index
 @posts = Post.page(params[:page]).per(10)
end

 Kaminariを追加したことで、各モデル上でpageとperというメソッドが使えるようになります。
pageメソッドの引数に現在のページ数を渡し、perの引数には何件でページを分割するかを渡します。
ここではperの引数に10を入れましたが、8件ずつページを区切りたいときは引数を8にします。

Viewの変更

 投稿一覧ページを表示する際には、each文を使うことが大体であると思いますが、rubyで用いる
<% end %> の後に <%= pagenate @posts %> と付け加えます。
 hamlを使った場合は、listの最後に = paginate @posts のように表記すると良いようです。
詳しくは、hamlを使って記事を書いている方がいらっしゃるので、そちらを参考にしてください。

views/posts/index.html.erb
 <% @posts.each do |post| %>
           .
           .
           .
 <% end %>
 <%= pagebate @posts %>

 ここまででミスがなければ、ページネーション用のリンクが自動で表示され、ページネーション機能が実装できます。
 しかし、見やすいデザインとまではなってないと思います。そこで、Bootstrapを利用して、デザインを整えます。すでにBootstrapを導入している方は、次のステップは飛ばしてください。

Bootstrapの導入

Bootstrapの導入には、yarnを利用してインストールするやり方、Bootstrapの公式サイトからダウンロードするやり方等ありますが、今回はCDNの方法で説明します。
ここの説明だけでは不安という方はhttps://www.youtube.com/watch?v=YY0mEyggH1E&t=713s
の動画で導入方法を説明しているものがありますので、そちらをご覧ください。

行うステップは2つ。
1つ目はheadタグの最後に、公式サイトにあるコードを記述。

layouts/application.html.erb
<head>
 <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">
</head>

2つ目はbodyタグの最後に、公式サイトにあるコードを記述

layouts/application.html.erb
<body>

 <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
 <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script>
 <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" integrity="sha384-B4gt1jrGC7Jh4AgTPSdUtOBvfO8shuf57BaghqFfPlYxofvL8/KUEfYiJOMMV+rV" crossorigin="anonymous"></script>

</body>

以上で導入が完了します。

Bootstrap4用のViewテンプレートを生成

 kaminariにはBootstrapやfoundationなどで使えるテーマが用意されています。
以下のコードをターミナルで実行すると「app/views/kaminari」配下にBootstrap4用のViewテンプレートが生成されます。このViewはkaminariデフォルトのViewより優先して適用されます。

ターミナル
rails g kaminari:views bootstrap4

ここまで完了すると見た目も良いデザインとなります。
うまくいかない時はサーバーを再起動してみてください。

また、英語の表記のままでは嫌だという人は日本語表記にする操作を行います。
「config/locales/kaminari_ja.yml」を追加し、以下のように記述します。

config/locales/kaminari_ja.yml
ja:
  views:
      pagination:
        first: '最初'
        last: '最後'
        previous: '前'
        next: '次'
        truncate: '...'

ここまでで日本語が適用されていないときは「config/application.rb」の中に config.i18n.default_locale = :ja を追加する。
全体的に見ると、以下のようになります。

config/application.rb
require_relative 'boot'

require 'rails/all'

# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)

module Theclo
  class Application < Rails::Application
    # Initialize configuration defaults for originally generated Rails version.
    config.load_defaults 5.2

    # Settings in config/environments/* take precedence over those specified here.
    # Application configuration can go into files in config/initializers
    # -- all .rb files in that directory are automatically loaded after loading
    # the framework and any gems in your application.
    config.i18n.default_locale = :ja
    config.time_zone = 'Tokyo'
  end
end

以上で実装完了です。
うまくいかない時は再度サーバーの再起動をしてみてください。
皆さんの参考になれば幸いです。

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

【Ruby on Rails】トップに戻るボタン

目標

arrow.gif

開発環境

ruby 2.5.7
Rails 5.2.4.3
OS: macOS Catalina

前提

※ ▶◯◯ を選択すると、説明等が出てきますので、
  よくわからない場合の参考にしていただければと思います。

上記機能はなくても大丈夫です。

流れ

1 写真の用意
2 viewの編集
3 cssの編集
4 jsファイルの編集(ページスクロールにアニメーションを追加する場合)

写真の用意

このような画像を用意してください。
ちなみにこの画像はipadで作成しましたので、ご自由にお使いください。
arrow.jpg

画像ファイルをapp/assets/images配下に格納してください。

viewの編集

今回は全てのページで表示したいため、下記場所に記載。

app/views/layouts/application.html.erb
<body>
  <%= yield %>
  <span id="back">
    <a href="">
      <%= image_tag asset_path('arrow.jpg'), data: {"turbolinks"=>false}, class: "arrow" %>
    </a>
  </span>
</body>

補足【image_tag asset_path( )】
app/assets/images配下の画像を読み込むことが出来ます。

補足【data: {"turbolinks"=>false}】
turbolinksの誤作動を防ぎます。

cssの編集

app/application.css
#back {
  position: fixed;
  right: 20px;
  bottom: 20px;
}
.arrow{
  width:      50px;
  height:     50px;
}

補足【position: fixed;】
position: fixed;により表示する場所を固定しています。

jsファイルの編集

gem 'jquery-rails'を導入しておいてください。
マウスでクリックするとアニメーションを動作させます。

app/assets/javascripts/application.js
$(function() {
  $('#back a').on('click',function(event){
    $('body, html').animate({
      scrollTop:0
    }, 800);
    event.preventDefault();
  });
});

補足【$('#back a').on('click',function(event)】
$('.セレクタ名').on('click',function(event) {  
イベント発生時に行われる処理
});

補足【$('body, html').animate】
$('セレクタ名').animate({   
変化対象のプロパティ名:変化値  
}, アニメーションの動作時間); 

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

vhと%の違い

※主に自分の振り返り用です。

クレジットカード登録画面の下部に空白が生じるのをなんとかしたい。
写真ではわかりにくいですが、黄色で囲んだところに生じている空白を
無くしたいと思い奮闘しました。

スクリーンショット 2020-09-23 10.21.22.png

いろいろ調べて、htmlやbodyに

height: 100%;

を当てる方法を試したところ、他のページのビューにも影響が出てしまい
この方法は断念。。。

このあと2時間ほど奮闘しましたが、
card_addクラスに

height: 100vh;

これであっさり解決。

要素の高さ指定で%とvhの違いがよく理解できていなかったので
ここにたどり着くのに時間がかかったようです。

vhと%の違い

【vh】
ページの表示領域に依存する。

height: 100vh;

と指定すれば、高さがページの表示領域いっぱいに表示される

【%】
親要素に依存する。

height: 100%;

と指定すれば親要素の大きさまで表示される。

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

No Method Errorについて

自分用のエラー解決録です。

フリマアプリ作成のチーム実装もいよいよ終盤。
各メンバーが開発したものをマスターにマージし、
自分のローカルにプルすると度々エラーが発生します。

今回のエラーはこちら
NoMethodError.png

マイページからログアウトページ遷移しようとしたところ発生。

undefined method `each' for nil:NilClass

という記述です。
場所はどこか?というと
user#showです。

早速コントローラーを確認すると

class UsersController < ApplicationController
  def index
    @parents = Category.where(ancestry: nil)
  end

  def show

  end

end

showの中身が空、、、
こちらに記述を追加

class UsersController < ApplicationController
  def index
    @parents = Category.where(ancestry: nil)
  end

  def show
    @parents = Category.where(ancestry: nil)
  end

end

するときちんと表示が戻りました。
スクリーンショット 2020-09-23 12.27.23.png

エラーを重ねることによって、エラー文から原因を自分で推測して
解決できるようになってきました。
自走力が少し身についてきたのかな?

まだまだこれからですが、引き続き頑張ります!

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

任意の場所から文字を取り出せるメソッド作成

【概要】

1.結論

2.どのように記載するか

3.ここから学んだこと

1.結論

lengthメソッドとsliceメソッドを使用する!


2.どのように記載するか

def srt_search(strings)
  character_number = strings.length #---❶
  character_reslut = strings.slice(character_number - 3, 3) #---❷
end

上記のように記述しました!

❶lengthで、文字列を数に変更しました。

ex)"Goodjob"➡︎"8"文字に変換

❷今回は最後から3番目までの数字が欲しいので❶の文字数からslice(character_number - 3, 3)することでcharacter_numberからの文字数から"-3"した番目の"3"文字分を取得できます。

ex)"Goodjob"の8文字から"-3"すると最初から"5"番目(=最後から3番目まで)の"3"文字を抜き取る。

参考にしたURL:
【Ruby入門説明書】ruby sliceについて解説

3.ここから学んだこと

slice(character_number - 3, 3)の部分は、
slice(-3,3)とすれば、最後から3番目の3文字を常にとることができるなと思いました。ただ、文章の構成(ex:Hi, John! Nice to meet you!)になると単語区分でsliceが判断してしまい、"to meet you!"が抜き出される点は注意しないといけません。

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

Rails 6で認証認可入り掲示板APIを構築する #18・終 user controllerの実装

Rails 6で認証認可入り掲示板APIを構築する #17 管理者権限の追加

全18回に渡る連載も今回で終了です。

今回はuser controllerを作ります。
今までの集大成なので、例示するコードを見ずに作ってみることをオススメします。

仕様

主に管理者がユーザー管理をする用途と、ユーザーが自分自身の更新削除をするための機能群として想定しています。

  • #index 管理者のみ閲覧可能
  • #show 自分自身か管理者のみ閲覧可能
  • #create 管理者のみ作成可能
  • #update 自分自身か管理者のみ更新可能
  • #destroy 管理者のみ削除可能

手順

  1. users_controllerの作成
  2. user_policyの作成
  3. user_policyテストの実装
  4. user_policyの実装
  5. users_controllerテストの実装
  6. users_controllerの実装

という手順で進めてみます。

1. users_controllerの作成

実装例

コマンド叩くだけなので簡単ですね。
$ rails g controller v1/users

rubocopに怒られないように微修正したファイル群がこちら。

app/controllers/v1/users_controller.rb
# frozen_string_literal: true

module V1
  #
  #  users controller
  #
  class UsersController < ApplicationController
  end
end

spec/requests/v1/users_request_spec.rb
# frozen_string_literal: true

require "rails_helper"

RSpec.describe "V1::Users", type: :request do
end

2. user_policyの作成

実装例

コマンド叩いてファイル作ります。

$ rails g pundit:policy user

rubocopに怒られないように微修正を加えたらとりあえず完成。

app/policies/user_policy.rb
# frozen_string_literal: true

#
# userのポリシークラス
#
class UserPolicy < ApplicationPolicy
  #
  # scope
  #
  class Scope < Scope
    def resolve
      scope.all
    end
  end
end
spec/policies/user_policy_spec.rb
# frozen_string_literal: true

require "rails_helper"

RSpec.describe UserPolicy, type: :policy do
  let(:user) { User.new }

  subject { described_class }

  permissions ".scope" do
    pending "add some examples to (or delete) #{__FILE__}"
  end

  permissions :show? do
    pending "add some examples to (or delete) #{__FILE__}"
  end

  permissions :create? do
    pending "add some examples to (or delete) #{__FILE__}"
  end

  permissions :update? do
    pending "add some examples to (or delete) #{__FILE__}"
  end

  permissions :destroy? do
    pending "add some examples to (or delete) #{__FILE__}"
  end
end

3. user_policyテストの実装

実装例

仕様のおさらい。

  • #index 管理者のみ閲覧可能
  • #show 自分自身か管理者のみ閲覧可能
  • #create 管理者のみ作成可能
  • #update 自分自身か管理者のみ更新可能
  • #destroy 管理者のみ削除可能

index, #create, #destroyで共通化、#show, #updateで共通化できそうですね。

それを念頭に実装したのがこちら。

spec/policies/user_policy_spec.rb
# frozen_string_literal: true

require "rails_helper"

RSpec.describe UserPolicy, type: :policy do
  let(:user) { create(:user) }
  let(:another_user) { create(:user) }
  let(:admin_user) { create(:user, :admin) }

  subject { described_class }

  permissions :index?, :create?, :destroy? do
    it "未ログインの時に不許可" do
      expect(subject).not_to permit(nil, user)
    end
    it "adminユーザー以外でログインしている時に不許可" do
      expect(subject).not_to permit(user, user)
    end
    it "adminユーザーでログインしている時に許可" do
      expect(subject).to permit(admin_user, user)
    end
  end

  permissions :show?, :update? do
    it "未ログインの時に不許可" do
      expect(subject).not_to permit(nil, user)
    end
    it "ログインしているが別ユーザーの時に不許可" do
      expect(subject).not_to permit(user, another_user)
    end
    it "adminユーザーでログインしている時に許可" do
      expect(subject).to permit(admin_user, user)
    end
    it "ログインしていて同一ユーザーの時に許可" do
      expect(subject).to permit(user, user)
    end
  end
end

another_userという別ユーザーを定義しているのがポイント。
さて、この時点ではpolicyを設定していないのでテストがいくつかコケることを確認してください。

4. user_policyの実装

実装例
app/policies/user_policy.rb
# frozen_string_literal: true

#
# userのポリシークラス
#
class UserPolicy < ApplicationPolicy
  def index?
    admin?
  end

  def show?
    me? || admin?
  end

  def create?
    admin?
  end

  def update?
    me? || admin?
  end

  def destroy?
    admin?
  end

  private

  def me?
    @record == @user
  end

  #
  # scope
  #
  class Scope < Scope
    def resolve
      scope.all
    end
  end
end

me?というprivateメソッドをuser_policy.rbに定義しているのがポイントです。

application_policy.rbにあるmine?は、@record.user == @userと、@recordの持つuserが自分のものか比較していました。
ですが今回は@record@userが一致するか確認するので、英語の文法的にmine?ではなくme?になります。その上、user自身と比較するのは現時点ではusers_controllerでしかなさそうなので、application_policyではなくuser_policyに提議しました。

あとは特筆することもなく、テストが通過するようになったはずです。

5. users_controllerテストの実装

実装例
spec/requests/v1/users_request_spec.rb
# frozen_string_literal: true

require "rails_helper"

RSpec.describe "V1::Users", type: :request do
  before do
    @user = create(:user, name: "userテスト")
    @authorized_headers = authorized_user_headers @user
    admin = create(:user, :admin)
    @authorized_admin_headers = authorized_user_headers admin
  end
  describe "GET /v1/users#index" do
    before do
      create_list(:user, 3)
    end
    it "正常レスポンスコードが返ってくる" do
      get v1_users_url, headers: @authorized_admin_headers
      expect(response.status).to eq 200
    end
    it "件数が正しく返ってくる" do
      get v1_users_url, headers: @authorized_admin_headers
      json = JSON.parse(response.body)
      expect(json["users"].length).to eq(3 + 2) # headers用2件を含む
    end
    it "id降順にレスポンスが返ってくる" do
      get v1_users_url, headers: @authorized_admin_headers
      json = JSON.parse(response.body)
      first_id = json["users"][0]["id"]
      expect(json["users"][1]["id"]).to eq(first_id - 1)
      expect(json["users"][2]["id"]).to eq(first_id - 2)
      expect(json["users"][3]["id"]).to eq(first_id - 3)
      expect(json["users"][4]["id"]).to eq(first_id - 4)
    end
  end

  describe "GET /v1/users#show" do
    it "正常レスポンスコードが返ってくる" do
      get v1_user_url({ id: @user.id }), headers: @authorized_headers
      expect(response.status).to eq 200
    end
    it "nameが正しく返ってくる" do
      get v1_user_url({ id: @user.id }), headers: @authorized_headers
      json = JSON.parse(response.body)
      expect(json["user"]["name"]).to eq("userテスト")
    end
    it "存在しないidの時に404レスポンスが返ってくる" do
      last_user = User.last
      get v1_user_url({ id: last_user.id + 1 }), headers: @authorized_headers
      expect(response.status).to eq 404
    end
  end

  describe "POST /v1/users#create" do
    let(:new_user) do
      attributes_for(:user, name: "create_nameテスト", email: "email+create_test@example.com", admin: true)
    end
    it "正常レスポンスコードが返ってくる" do
      post v1_users_url, params: new_user, headers: @authorized_admin_headers
      expect(response.status).to eq 200
    end
    it "1件増えて返ってくる" do
      expect do
        post v1_users_url, params: new_user, headers: @authorized_admin_headers
      end.to change { User.count }.by(1)
    end
    it "name, email, adminが正しく返ってくる" do
      post v1_users_url, params: new_user, headers: @authorized_admin_headers
      json = JSON.parse(response.body)
      expect(json["user"]["name"]).to eq("create_nameテスト")
      expect(json["user"]["email"]).to eq("email+create_test@example.com")
      expect(json["user"]["admin"]).to be true
    end
    it "不正パラメータの時にerrorsが返ってくる" do
      post v1_users_url, params: {}, headers: @authorized_admin_headers
      json = JSON.parse(response.body)
      expect(json.key?("errors")).to be true
    end
  end

  describe "PUT /v1/users#update" do
    let(:update_param) do
      update_param = attributes_for(:user, name: "update_nameテスト", email: "email+update_test@example.com", admin: true)
      update_param[:id] = @user.id
      update_param
    end
    it "正常レスポンスコードが返ってくる" do
      put v1_user_url({ id: update_param[:id] }), params: update_param, headers: @authorized_headers
      expect(response.status).to eq 200
    end
    it "name, email, adminが正しく返ってくる" do
      put v1_user_url({ id: update_param[:id] }), params: update_param, headers: @authorized_headers
      json = JSON.parse(response.body)
      expect(json["user"]["name"]).to eq("update_nameテスト")
      expect(json["user"]["email"]).to eq("email+update_test@example.com")
      expect(json["user"]["admin"]).to be false # admin権限は書き換えできると困るのでfalseのまま
    end
    it "不正パラメータの時にerrorsが返ってくる" do
      put v1_user_url({ id: update_param[:id] }), params: { name: "" }, headers: @authorized_headers
      json = JSON.parse(response.body)
      expect(json.key?("errors")).to be true
    end
    it "存在しないidの時に404レスポンスが返ってくる" do
      last_user = User.last
      put v1_user_url({ id: last_user.id + 1 }), params: update_param, headers: @authorized_admin_headers
      expect(response.status).to eq 404
    end
  end

  describe "DELETE /v1/users#destroy" do
    it "正常レスポンスコードが返ってくる" do
      delete v1_user_url({ id: @user.id }), headers: @authorized_admin_headers
      expect(response.status).to eq 200
    end
    it "1件減って返ってくる" do
      expect do
        delete v1_user_url({ id: @user.id }), headers: @authorized_admin_headers
      end.to change { User.count }.by(-1)
    end
    it "存在しないidの時に404レスポンスが返ってくる" do
      last_user = User.last
      delete v1_user_url({ id: last_user.id + 1 }), headers: @authorized_admin_headers
      expect(response.status).to eq 404
    end
  end
end

いくつかpostの時とは違う考慮点があります。

  • headerを作る際にcreate(:user)をしているので、その2件分を考慮してテストを書くこと。
    • 存在しないid確認の際も同様、last_userを取得して+1
  • adminフラグは自由に書き換えれてしまうと困るので、createでadminフラグは反映されるがupdateでは反映されないこと

あたりを取り入れています。

また、レスポンスにadmin判定があるので、serializerの修正も必要ですね。

app/serializers/user_serializer.rb
 # user serializer
 #
 class UserSerializer < ActiveModel::Serializer
-  attributes :id, :name, :email
+  attributes :id, :name, :email, :admin
 end

以上で準備完了。次にcontroller実装に入ります。

6. users_controllerの実装

実装例

controllerにアクセスできるようにするため、routesの修正をします。

config/routes.rb
 Rails.application.routes.draw do
   namespace "v1" do
     resources :posts
+    resources :users
     mount_devise_token_auth_for "User", at: "auth"
   end

続いてcontrollerです。
こちらはpostのほぼ流用でいけますね。

app/controllers/v1/users_controller.rb
# frozen_string_literal: true

module V1
  #
  #  users controller
  #
  class UsersController < ApplicationController
    before_action :set_user, only: %i[show update destroy]

    def index
      users = User.order(created_at: :desc).limit(20)
      authorize users
      render json: users
    end

    def show
      authorize @user
      render json: @user
    end

    def create
      user = User.new(user_create_params)
      user[:provider] = :email
      authorize user
      if user.save
        render json: user
      else
        render json: { errors: user.errors }
      end
    end

    def update
      authorize @user
      if @user.update(user_params)
        render json: @user
      else
        render json: { errors: @user.errors }
      end
    end

    def destroy
      authorize @user
      @user.destroy
      render json: @user
    end

    private

    def set_user
      @user = User.find(params[:id])
    end

    def user_create_params
      # createの時のみadmin権限を設定できるようにする
      params.permit(:name, :email, :admin, :password)
    end

    def user_params
      params.permit(:name, :email, :password)
    end
  end
end

テストのセクションで書いたように、adminはcreateの時のみ付与できるようにしています。

以上です。
全18回に渡りご覧いただきありがとうございました。

ぜひこれを元に、コメント機能やソーシャルログイン等の機能拡張していってください。

目次

連載目次へ

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

SREエンジニア採用面接における質問と趣旨

概要

会社では週に数回、採用面接を担当してるのですが、最近ではフロントエンド・バックエンドだけでなくSREの採用も執り行うようになりました。
ここではSREを採用する際に実際に私が面接で質問する主な観点と趣旨について公開していこうと思います。

誰が書いてるか

現職は メタップス にて横断的なテックリードやSREエンジニアをしてます。学生の頃に「ユニバーサルHTML/XHTML」やヤコブ・ニールセンの「ウェブ・ユーザビリティ」に感化されフロントエンド技術に興味を持ち始め、その後バックエンドに携わるとミーハーらしくPoEAAやDDDにハマり、勢いに乗ってベトナム・ハノイでのオフショア事業を立ち上げました。自称フルスタックしつつ、現地メンバーの採用を始め育成計画やらアーキテクトとして10年ほど携わり、今に至ります。ここ数年の興味はもっぱらインフラで、SREとしての動きが主軸となりつつあります。

SREに求めるスキル

採用ページから抜粋。AWS DevDay 2019 で登壇したときの技術構成がベースとなってます。

技術的な必須要件

  • AWSを利用したインフラ設計・構築・運用経験
  • LinuxやDockerの運用経験
  • クラウドネイティブなアプリケーションの設計・開発
  • OSやミドルウェア、データベースのチューニング
  • Ruby on RailsやNode.jsを使ったアプリケーションの開発

技術的な歓迎要件

  • オーケストレーションツールを利用したインフラ構築
  • OSやミドルウェアの検証・導入
  • 大規模トラフィックを捌くインフラ基盤設計の経験
  • インフラ運用の効率化・自動化
  • 監査・情報セキュリティに関する知見
  • OSSの公開、コントリビュート経験

利用技術

  • クラウド: AWS
  • OS: Linux
  • 仮想化: ECS
  • データベース: RDS・ElastiCache・ElastiCache・DynamoDB
  • 構成管理: Terraform
  • 開発言語: Ruby on Rails・Node.js
  • 監視: Datadog
  • ログ: Fluentd・Elasticsearch Service・Kibana

選考プロセス

  1. 書類選考
  2. カジュアル面談 (開発メンバーとの面談)
  3. 一次面談 (開発リーダーとの面談)
  4. 二次面談 (事業責任者・開発責任者との面談)

カジュアル面談は、選考に入る前に事業の紹介や候補者との情報交換のために設けられます。必ず実施するとは限りません。

どのフェーズで面接に関わっているか

主に人事がピックアップしてくれた方を中心に二次面接に関わることが多いです。
ただエンジニア経験が浅くても、ポートフォリオなどから会ってみたい方は割といるので、自分からオファーをかけることもあります。

二次面接はどのような流れで進むか

  1. 挨拶
  2. 事業紹介 (事業責任者)
  3. SREの役割について説明 (開発責任者)
  4. 候補者の自己紹介
  5. 候補者からの質疑応答
  6. 技術面のスキルチェック (開発責任者)
  7. 候補者からの質疑応答

今回は「6. 技術面のスキルチェック」について深堀りします。

履歴書はどこまで見ているか

年齢と自己PRくらい。

職務経歴書はどこまで見ているか

仕事内容よりも、「どういったスキルをどの場面で、どのように活用したか」、「どのようなポジションで仕事に携わっていたか。周りとの関係性 (コミュニケーション面)」を見ることが多いです。

ポートフォリオは必要か

重要視してます。職務経歴書よりもソースコードから読み取れるスキル・経験則のほうが情報量が多いためです。QiitaやGitHub、Speaker Deckなど、公開できるリソースはどんどん公開していきましょう。

ケーススタディはあるか

あります。ここでは質問に対する答えの論理性や整合性を見ます。SREはアプリケーションエンジニアと密にコミュニケーションを取るため、説明の伝え方やわかり易さ、積極性も見てます。

コーディング試験はあるか

現状実施してませんが、将来的に取り入れる可能性はあります。

質問の観点と意図

本題。

アーキテクチャ設計の経験

  • 趣旨
    • SREはインフラのみ知識だけあれば良い訳ではなく、アプリケーション設計や開発経験も含めた総合的な知見や経験が求められます。Webシステムにおけるアーキテクチャの設計経験があれば、システムのどこにボトルネックがあり、問題を改善する上でどういった工夫を施したか、など詳しく知見を引き出すことができます
  • 付随する質問事項
    • アーキテクチャの概要説明
    • システムの規模
      • MAU、トラフィックなど
    • 影響を受けたアーキテクチャ
      • DDD、マイクロサービス、Twelve-Factor Appなど
    • APIの設計
      • Open API、Graph API、gRPCなど
      • 選定理由

アプリケーション開発の経験

  • 趣旨
    • SREはインフラの運用・自動化を推進しますが、日々構成をアップデートしていく中で、アプリケーションコードにも手を入れなければならない場面が出てきます。例えば、マイクロサービスの統廃合であったり、システムリプレースにおける開発環境の仮想化・インテグレーションなどが当たります。アプリケーションエンジニアにはサービス開発に専念して欲しいため、インフラと密になりやすいタスクはSREがカバーする方針で運用しています
  • 付随する質問事項
    • 得意とする言語 (理由)・経験年数
      • 不得意な言語があればその理由
    • 利用したことのあるフレームワーク・それぞれの利点と問題点
    • パフォーマンス最適化の経験
      • 技術構成
    • フロントエンド・バックエンドのどちらを得意とするか (システムを最適化する上でどちらを得意とするか)
    • テストコードやレビューの経験
      • レビュワーとしての観点

DB設計の経験

  • 趣旨
    • Webシステムでボトルネックとなる大半の要因はDBの設計やクエリの書き方にあります。SREはサービスを監視する上でネットワークのレイテンシも追跡しています。メトリクスに異常があれば、APMなどを介して問題のコードを洗い出し、クエリの実行計画を分析した上でアプリケーションエンジニアに共有するスキルが求められます
  • 付随する質問事項
    • 経験のあるDBエンジン
    • チューニング経験
      • どのように? (ハード・ソフト)
      • トランザクション数・テーブル数

SREとしての経験

  • 趣旨
    • フロントエンドやバックエンド開発もある中で、何故SREを希望しているのか。SREとして将来的に何を目標として仕事をしたいか。前職までの経験で培ったスキル・その時々の課題などをヒアリングし、会社が求めている人材と一致するかを確認します
  • 付随する質問事項
    • ネットワークやサーバ・ミドルウェア構築の経験
      • オンプレミスの経験
    • スパイクアクセス
      • トラフィックの算定方法
      • どのような対策を施したか・結果はどうだったか
    • AWS/GCPの経験
      • 好きなサービス
      • 興味のあるサービス
    • 運用
      • 自動化の経験・どのように改善したか
    • 監視
      • 利用したことのあるツール・選定基準
      • オンコール対応の経験
    • IaC
      • 設計・構築の経験
        • 設計パターンの具体例
      • 利用したことのあるツール・選定基準
    • ログ分析
      • 設計・構築の経験
      • アーキテクチャ構成・課題
    • CI/CD・インフラ運用の自動化
      • 設計・構築経験
      • アーキテクチャ構成・課題
    • セキュリティ
      • アプリケーションのセキュリティ対策
      • コンテナに対するセキュリティの知見
      • IPS/IDSの知見
    • 監査
      • ガイドライン策定の経験
      • インシデント管理

雑談

  • 趣旨
    • 開発に対する意欲や将来的に目指すキャリアプランをヒアリングし、事業メンバーとフィーリングが一致するかを確認します
  • 付随する質問事項
    • 転職理由
    • 自身のスキルの優位性
    • 自作でサービスを作ったことがあるか
      • 使用言語・開発期間
      • 成果と課題
    • 興味のある技術
    • OSSコントリビュート経験
      • 自主的にシステムの課題を発見し、解決するスキルがあるか
    • Qiitaなどでの技術公開・勉強会などでの登壇経験
      • 情報の発信力。技術をアウトプットできるか
    • 入社後に挑戦したいこと
    • キャリアプラン
      • VPoE or CTO?
    • 最終的に入社の決め手となる要素は何か

まとめ

SREという職種は比較的新しく、人手が不足している職種の一つかと思います。サービスをインフラ・アプリケーション両方の観点から成長させていく楽しみもあるので、是非転職の際は検討してみてください!

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

SREエンジニア面接における質問と趣旨を公開する

概要

会社では週に数回、採用面接を担当してるのですが、最近ではフロントエンド・バックエンドだけでなくSREの採用も執り行うようになりました。
ここではSREを採用する際に実際に私が面接で質問する主な観点と趣旨について公開していこうと思います。

誰が書いてるか

現職は メタップス にてテックリードやSREエンジニアをしてます。学生の頃に「ユニバーサルHTML/XHTML」やヤコブ・ニールセンの「ウェブ・ユーザビリティ」に感化されフロントエンド技術に興味を持ち始め、その後バックエンドに携わるとミーハーらしくPoEAAやDDDにハマり、勢いに乗ってベトナム・ハノイでのオフショア事業を立ち上げました。自称フルスタックしつつ、現地メンバーの採用を始め育成計画やらアーキテクトとして10年ほど携わり、今に至ります。ここ数年の興味はもっぱらインフラで、SREとしての動きがメインとなりつつあります。

SREに求めるスキル

採用ページから抜粋。AWS DevDay 2019 で登壇したときの技術構成がベースとなってます。

技術的な必須要件

  • AWSを利用したインフラ設計・構築・運用経験
  • LinuxやDockerの運用経験
  • クラウドネイティブなアプリケーションの設計・開発
  • OSやミドルウェア、データベースのチューニング
  • Ruby on RailsやNode.jsを使ったアプリケーションの開発

技術的な歓迎要件

  • オーケストレーションツールを利用したインフラ構築
  • OSやミドルウェアの検証・導入
  • 大規模トラフィックを捌くインフラ基盤設計の経験
  • インフラ運用の効率化・自動化
  • 監査・情報セキュリティに関する知見
  • OSSの公開、コントリビュート経験

利用技術

  • クラウド: AWS
  • OS: Linux
  • 仮想化: ECS
  • データベース: RDS・ElastiCache・ElastiCache・DynamoDB
  • 構成管理: Terraform
  • 開発言語: Ruby on Rails・Node.js
  • 監視: Datadog
  • ログ: Fluentd・Elasticsearch Service・Kibana

どのフェーズで面接に関わっているか

主に人事がピックアップしてくれた方を中心に最終面接に関わることが多いです。
ただエンジニア経験が浅くても、ポートフォリオなどから会ってみたい方は割といるので、自分からオファーをかけることもあります。

履歴書はどこまで見ているか

名前と年齢くらい。

職務経歴書はどこまで見ているか

仕事内容よりも、「どういったスキルをどの場面で、どのように活用したか」、「どのようなポジションで仕事に携わっていたか。周りとの関係性 (コミュニケーション面)」を見ることが多いです。

ポートフォリオは必要か

重要視してます。職務経歴書よりもソースコードから読み取れるスキル・経験則のほうが情報量が多いためです。QiitaやGitHub、Speaker Deckなど、公開できるリソースはどんどん公開していきましょう。

ケーススタディやコードテストはあるか

ケーススタディはあります。ここでは質問に対する答えの論理性・整合性を見ます。SREはアプリケーションエンジニアと密にコミュニケーションを取るため、説明の伝え方やわかり易さ、能動性も見ています。
また、現状コードテストは実施してませんが、将来的に取り入れる可能性はあります。

質問の観点と意図

ようやく本題。

アーキテクチャ設計の経験

  • 趣旨
    • SREはインフラのみ理解しておけば良い訳ではなく、アプリケーション設計や開発経験も含めた総合的な知見やセンスが求められます。Webシステムにおけるアーキテクチャの設計経験があれば、システムのどこにボトルネックがあり、問題を改善する上でどういった工夫を施したか、など詳しく知見を引き出すことができます
  • 付随する質問事項
    • システムの規模
      • MAU、トラフィックなど
    • 影響を受けたアーキテクチャ
      • DDD、マイクロサービス、Twelve-Factor Appなど
    • APIの設計
      • Open API、Graph API、gRPCなど
      • 選定理由

アプリケーション開発の経験

  • 趣旨
    • SREはインフラの運用・自動化を推進しますが、日々構成をアップデートしていく中で、アプリケーションコードにも手を入れなければならない場面が出てきます。例えば、マイクロサービスの統廃合であったり、システムリプレースにおける開発環境の仮想化・インテグレーションなどが当たります。アプリケーションエンジニアにはサービス開発に専念して欲しいため、インフラと密になりやすいタスクはSREがサポートする方針で運用しています
  • 付随する質問事項
    • 得意とする言語 (理由)・経験年数
      • 不得意な言語があればその理由
    • 利用したことのあるフレームワーク・それぞれの利点と問題点
    • パフォーマンス最適化の経験
      • 技術構成
    • フロントエンド・バックエンドのどちらを得意とするか
    • テストコードやレビューの経験
      • レビュワーとしての観点

DB設計の経験

  • 趣旨
    • Webシステムでボトルネックとなる大半の要因はDBの設計やクエリの書き方にあります。SREはサービスを監視する上でネットワークのレイテンシも追跡しています。メトリクスに異常があれば、APMなどを介して問題のコードを洗い出し、クエリの実行計画を分析した上でアプリケーションエンジニアに共有するスキルが求められます
  • 付随する質問事項
    • 経験のあるDBエンジン
    • チューニング経験
      • どのように? (ハード/ソフト)
      • トランザクション数・テーブル数

SREとしての経験

  • 趣旨
    • フロントエンドやバックエンド開発もある中で、何故SREを希望しているのか。SREとして将来的に何を目標として仕事をしたいか。前職までの経験で培ったスキル・その時々の課題などをヒアリングし、会社が求めている人材と一致するかを確認します
  • 付随する質問事項
    • ネットワークやサーバ構築の経験
      • オンプレミスの経験
    • AWS/GCPの経験
      • 好きなサービス
      • 興味のあるサービス
    • IaC
      • 設計・構築の経験
      • 利用したことのあるツール・選定基準
    • ログ分析
      • 設計・構築の経験
      • アーキテクチャ構成・課題
    • CI/CD・インフラ運用の自動化
      • 設計・構築経験
      • アーキテクチャ構成・課題
    • ドキュメント管理
      • 使用するツール
      • インシデント管理
      • ガイドライン策定の経験

雑談

  • 趣旨
    • 開発に対する意欲や将来的に目指すキャリアプランをヒアリングし、事業メンバーとフィーリングが一致するかを確認します
  • 付随する質問事項
    • 転職理由
    • 自身のスキルの優位性
    • 自作でサービスを作ったことがあるか
      • 使用言語・開発期間
      • 成果と課題
    • OSSコントリビュート経験
      • 自主的にシステムの課題を発見し、解決するスキルがあるか
    • Qiitaなどでの技術公開・勉強会などでの登壇経験
      • 情報の発信力。技術をアウトプットできるか
    • 入社後に挑戦したいこと
    • キャリアプラン
      • VPoE or CTO?
    • 最終的に入社の決め手となる要素は何か

まとめ

SREという職種は比較的新しく、人手が不足している職種の一つかと思います。サービスをインフラ・アプリケーション両方の観点から成長させていく楽しみもあるので、是非転職の際は検討してみてください!

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

SREエンジニア面接における質問と趣旨

概要

会社では週に数回、採用面接を担当してるのですが、最近ではフロントエンド・バックエンドだけでなくSREの採用も執り行うようになりました。
ここではSREを採用する際に実際に私が面接で質問する主な観点と趣旨について公開していこうと思います。

誰が書いてるか

現職は メタップス にて横断的なテックリードやSREエンジニアをしてます。学生の頃に「ユニバーサルHTML/XHTML」やヤコブ・ニールセンの「ウェブ・ユーザビリティ」に感化されフロントエンド技術に興味を持ち始め、その後バックエンドに携わるとミーハーらしくPoEAAやDDDにハマり、勢いに乗ってベトナム・ハノイでのオフショア事業を立ち上げました。自称フルスタックしつつ、現地メンバーの採用を始め育成計画やらアーキテクトとして10年ほど携わり、今に至ります。ここ数年の興味はもっぱらインフラで、SREとしての動きが主軸となりつつあります。

SREに求めるスキル

採用ページから抜粋。AWS DevDay 2019 で登壇したときの技術構成がベースとなってます。

技術的な必須要件

  • AWSを利用したインフラ設計・構築・運用経験
  • LinuxやDockerの運用経験
  • クラウドネイティブなアプリケーションの設計・開発
  • OSやミドルウェア、データベースのチューニング
  • Ruby on RailsやNode.jsを使ったアプリケーションの開発

技術的な歓迎要件

  • オーケストレーションツールを利用したインフラ構築
  • OSやミドルウェアの検証・導入
  • 大規模トラフィックを捌くインフラ基盤設計の経験
  • インフラ運用の効率化・自動化
  • 監査・情報セキュリティに関する知見
  • OSSの公開、コントリビュート経験

利用技術

  • クラウド: AWS
  • OS: Linux
  • 仮想化: ECS
  • データベース: RDS・ElastiCache・ElastiCache・DynamoDB
  • 構成管理: Terraform
  • 開発言語: Ruby on Rails・Node.js
  • 監視: Datadog
  • ログ: Fluentd・Elasticsearch Service・Kibana

選考プロセス

  1. 書類選考
  2. カジュアル面談 (開発メンバーとの面談)
  3. 一次面談 (開発リーダーとの面談)
  4. 二次面談 (事業責任者・開発責任者との面談)

カジュアル面談は、選考に入る前に事業の紹介や候補者との情報交換のために設けられます。必ず実施するとは限りません。

どのフェーズで面接に関わっているか

主に人事がピックアップしてくれた方を中心に二次面接に関わることが多いです。
ただエンジニア経験が浅くても、ポートフォリオなどから会ってみたい方は割といるので、自分からオファーをかけることもあります。

二次面接はどのような流れで進むか

  1. 挨拶
  2. 事業紹介 (事業責任者)
  3. SREの役割について説明 (開発責任者)
  4. 候補者の自己紹介
  5. 候補者からの質疑応答
  6. 技術面のスキルチェック (開発責任者)
  7. 候補者からの質疑応答

今回は「6. 技術面のスキルチェック」について深堀りします。

履歴書はどこまで見ているか

年齢と自己PRくらい。

職務経歴書はどこまで見ているか

仕事内容よりも、「どういったスキルをどの場面で、どのように活用したか」、「どのようなポジションで仕事に携わっていたか。周りとの関係性 (コミュニケーション面)」を見ることが多いです。

ポートフォリオは必要か

重要視してます。職務経歴書よりもソースコードから読み取れるスキル・経験則のほうが情報量が多いためです。QiitaやGitHub、Speaker Deckなど、公開できるリソースはどんどん公開していきましょう。

ケーススタディはあるか

あります。ここでは質問に対する答えの論理性・整合性を見ます。SREはアプリケーションエンジニアと密にコミュニケーションを取るため、説明の伝え方やわかり易さ、積極性も見てます。

コーディング試験はあるか

現状実施してませんが、将来的に取り入れる可能性はあります。

質問の観点と意図

ようやく本題。

アーキテクチャ設計の経験

  • 趣旨
    • SREはインフラのみ知識だけあれば良い訳ではなく、アプリケーション設計や開発経験も含めた総合的な知見や経験が求められます。Webシステムにおけるアーキテクチャの設計経験があれば、システムのどこにボトルネックがあり、問題を改善する上でどういった工夫を施したか、など詳しく知見を引き出すことができます
  • 付随する質問事項
    • システムの規模
      • MAU、トラフィックなど
    • 影響を受けたアーキテクチャ
      • DDD、マイクロサービス、Twelve-Factor Appなど
    • APIの設計
      • Open API、Graph API、gRPCなど
      • 選定理由

アプリケーション開発の経験

  • 趣旨
    • SREはインフラの運用・自動化を推進しますが、日々構成をアップデートしていく中で、アプリケーションコードにも手を入れなければならない場面が出てきます。例えば、マイクロサービスの統廃合であったり、システムリプレースにおける開発環境の仮想化・インテグレーションなどが当たります。アプリケーションエンジニアにはサービス開発に専念して欲しいため、インフラと密になりやすいタスクはSREがカバーする方針で運用しています
  • 付随する質問事項
    • 得意とする言語 (理由)・経験年数
      • 不得意な言語があればその理由
    • 利用したことのあるフレームワーク・それぞれの利点と問題点
    • パフォーマンス最適化の経験
      • 技術構成
    • フロントエンド・バックエンドのどちらを得意とするか (アプリケーションのパフォーマンスを最適化する上でどちらを得意とするか)
    • テストコードやレビューの経験
      • レビュワーとしての観点

DB設計の経験

  • 趣旨
    • Webシステムでボトルネックとなる大半の要因はDBの設計やクエリの書き方にあります。SREはサービスを監視する上でネットワークのレイテンシも追跡しています。メトリクスに異常があれば、APMなどを介して問題のコードを洗い出し、クエリの実行計画を分析した上でアプリケーションエンジニアに共有するスキルが求められます
  • 付随する質問事項
    • 経験のあるDBエンジン
    • チューニング経験
      • どのように? (ハード/ソフト)
      • トランザクション数・テーブル数

SREとしての経験

  • 趣旨
    • フロントエンドやバックエンド開発もある中で、何故SREを希望しているのか。SREとして将来的に何を目標として仕事をしたいか。前職までの経験で培ったスキル・その時々の課題などをヒアリングし、会社が求めている人材と一致するかを確認します
  • 付随する質問事項
    • ネットワークやサーバ・ミドルウェア構築の経験
      • オンプレミスの経験
    • スパイクアクセス
      • トラフィックの算定方法
      • どのような対策を施したか・結果はどうだったか
    • AWS/GCPの経験
      • 好きなサービス
      • 興味のあるサービス
    • 監視
      • 利用したことのあるツール・選定基準
      • オンコール対応の経験
    • IaC
      • 設計・構築の経験
        • 設計パターンの具体例
      • 利用したことのあるツール・選定基準
    • ログ分析
      • 設計・構築の経験
      • アーキテクチャ構成・課題
    • CI/CD・インフラ運用の自動化
      • 設計・構築経験
      • アーキテクチャ構成・課題
    • セキュリティ
      • アプリケーションのセキュリティ対策
      • コンテナに対するセキュリティの知見
      • IPS/IDSの知見
    • 監査
      • ガイドライン策定の経験
      • インシデント管理

雑談

  • 趣旨
    • 開発に対する意欲や将来的に目指すキャリアプランをヒアリングし、事業メンバーとフィーリングが一致するかを確認します
  • 付随する質問事項
    • 転職理由
    • 自身のスキルの優位性
    • 自作でサービスを作ったことがあるか
      • 使用言語・開発期間
      • 成果と課題
    • 興味のある技術
    • OSSコントリビュート経験
      • 自主的にシステムの課題を発見し、解決するスキルがあるか
    • Qiitaなどでの技術公開・勉強会などでの登壇経験
      • 情報の発信力。技術をアウトプットできるか
    • 入社後に挑戦したいこと
    • キャリアプラン
      • VPoE or CTO?
    • 最終的に入社の決め手となる要素は何か

まとめ

SREという職種は比較的新しく、人手が不足している職種の一つかと思います。サービスをインフラ・アプリケーション両方の観点から成長させていく楽しみもあるので、是非転職の際は検討してみてください!

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