しふみんのブログ

しふみんのブログです。

エンジニア採用面接初心者が参考にするリンク集

近々採用面接担当としての仕事が降ってくる気配あるので、そのためのインプットを行なっている。
せっかくだからその参考リンク集をHackMDに書き出した。
まだしばらくはインプットは続くはずなので更新しやすいHackMDに切り出した。

hackmd.io

株式会社グロービスに入社しました

正確には「株式会社グロービスにエンジニアとして入社して3ヶ月が経ちました」です。 晴れて試用期間が満了したことと、同日‪の4月1日に入社して同じチームで働いている‪ @mktakuya ‬が入社エントリを書いたので後に続こうというお気持ちです。

blog.m6a.jp

あと、つよつよエンジニアの転職エントリはそこらに転がっているけど、そうではないエンジニアの転職エントリも僕は読みたいなと思ったので自分で書くことにしました。

で、誰?

  • @shifumin
  • 新卒で地元のジャパニーズトラディショナルローカルガバメント(notエンジニア) -> 小さなWeb系受託開発会社 -> グロービス
  • Railsエンジニア
  • 主にAPIを作ったりデータモデリング周りを触っていた
  • グロービスに入社してReactも触り始めた

グロービスという会社

recruiting-tech.globis.co.jp

グロービスと聞いて思い浮かぶのは、経営大学院、グロービスMBAシリーズの書籍、法人向け研修、VCといったところでしょうか。
これら(だけではないけど)に加えて、グロービスは2016年にGDP(グロービス・デジタル・プラットフ ォーム)という部署を組織してEdTechの領域に参入しました。
現在提供しているサービスは大きくくくると「グロービス学び放題」と「グロービス・ラーニング・プラットフォーム」で、前者はビジネススキルを学べる定額型動画学習サービス、後者はグロービスの提供している研修・学習システムを集約し一括管理するためのプラットフォームです。
社内のエンジニアの大部分はGDPに集まっています。

入社の経緯

前職は受託開発の会社でずっと自社内でクライアントワークをカタカタやっていたのですが、社内のやむごとなき理由によりある日表参道名刺管理アプリ社にSESとして客先常駐することになりました。
そこで初めてスクラムを体験してプロダクトだけでなく開発プロセスの改善等を通じてチームも成長していくところに「これや!」感を感じ、当事者として関わりたいという思いとともに転職活動を始めました。

業種は特にこだわりはなかったのですが、森博嗣の「今はもうない」の次の一節

舗装された林道が開通し、そこを走る大型トラックによる輸送が、おそらくこの軽便鉄道の使命を奪ったのであろう。廃線になって十年、いやそれ以上経っているに違いない。しかし、別にどうということはない。いつだって、より効率の良いものが、どんなに親しまれ、どんなに美しく伝統的な手法にも、必ず勝る。それがシステムというものだ。ようするに楽であること……、それ以外に人間を魅了するものはない、といっても過言ではない。

はてなダイアリー時代のブログのヘッダーにずっと貼っていたくらい大好きで、要は利用する人が楽できるプロダクトの開発に関わりたい(あわよくば自分も楽したい)という気持ちがあったのと、広告主が見え隠れすると意識がブレそうなので「ビジネスモデルが広告収益モデルでないユーザーから直接お金を頂く」ビジネスモデルの会社がいいなという気持ちと、あとバックエンドがRailsならすぐに力になれそうだという全てを満たすような会社に話を聞きに行きました。ついでに転職ドラフトで指名いただいた会社はとりあえず話を聞きに行ったのでトータルで15社くらいのカジュアル面談を受けた気がします。

カジュアル面談で色々な会社の話を聞きに行ったはいいもののじゃあどの会社の本選考を受けるかの決め手がなくて悩んでいたところ、たまたま去年の僕の誕生日にQiitaが開催していたCareer Meetupに参加して、 @sue738グロービスを紹介している発表を聞くことができました。

logmi.jp

元々学ぶことが好きだったことと、GDPは今参加しても面白そうなフェイズだなと感じたことと、上で上げた条件を全て満たしていることの全てが線で繋がったのでこの船に乗るしかないなと思い選考を受けた結果、ご縁がありグロービスに入社する運びとなりました。
僕は地方出身なのですが、東京に来てから地方って学習モチベがあってもそのため環境が都会に比べて整っていないことを強く実感するようになって、それをどうにかして是正したいということを選考で熱く語った記憶があります。

何をしているか

グロービス・ラーニング・プラットフォームの開発に携わっています。主にはAPIを作成したり、他サービスとの連携部分のコードを書いています。
あと、チームの生産性向上やスクラムマスターに興味ありますと言っていたらスクラムマスターのロールが降ってきたのでスクラムマスターも始めました。

社内の雰囲気

働きやすいです。
内製の開発組織が全くなかった状態から2年数ヶ月でこのエンジニア組織とその制度を整えるのは正直すごいなということをまず思いました。
フレックスタイム、リモートワーク、副業許可、壁一面ホワイトボード、ソファスペース等Web系ベンチャーでよくありそうな制度は大体用意されています。
教育事業を行っているだけあって、エンジニア自体学習意欲が高めな人が多いことを差し置いても学習意欲の高い人が多いと感じました。

もちろん課題もあって、個人的には下記を感じます。

  • 組織拡大による人数の増加・チームの分割等の影響でbiz側dev側の期待値調整・優先順位のすり合わせが弱いことがある
  • サービス開始から数年経ち技術的負債が溜まり始めた

前者はフィットする会議体、組織構成を変化させながら塩梅を模索中、後者は機能開発とバランスを取りながら負債返却にメスが入り始めた状況です。

いい感じに課題が転がっていてまだまだ人が足りなくエンジニアを絶賛募集中なので興味ある方はぜひ下記あたりから応募しましょう!

www.wantedly.com

全方位で募集しているけど、特にiOS、QA、フロントエンドのエンジニアが欲しい雰囲気が社内にあります。 また、色々と中のことをお話しすることもできますのでぜひご飯でも誘ってください。

現場からは以上です。

あとでここ見てって言う用のあれこれ

お金

皆大好きお金の話ですが、アカウントを作れば誰でも指名額を見れる 転職ドラフトによると大体の提示額が分かるかと思います。リードクラスのエンジニアの採用チャネルとして転職ドラフトを利用しているようです。 残念ながら僕は転職ドラフト経由ではないことと1人しか同僚の年収の正確な額を知らないので、実態はよく分かりません。。。

その他

  • Q.エンジニアはどれくらいいる?
    • A.GDPにいるエンジニアは50人くらい(業務委託含む)。毎月2人ずつくらい入社している印象。
  • Q.支給マシン等は?
    • A.MacBook Pro15インチマシマシとDellの27インチディスプレイ(not 4k)1枚
  • Q.リモートワークやフレックスは利用されている?
    • A.""自由と自己責任""に則って結構利用されていると思う。「作業集中のため終日RWします」「荷物受け取りで午前RW、午後から出社します」「通院のため早退します」あたりは毎日のようにSlackの勤怠chで見る。
  • Q.勤怠はどんな感じ?
    • A.10時台に来て19時台に帰る人が多いイメージ。みなし残業45時間だけど、実感として残業月10時間くらいのイメージがある(所定は7.5 時間/日)
      • 以下、私の勤怠例。左が標準的な週、右が色々やっていた週の出勤時間と退勤時間。
      • Image from GyazoImage from Gyazo

create_tableでカラムを定義するのと同時にユニークインデックスを貼るやつ

なぜかレビューを通っていた下記のようなmigrationがあったのですが、 unique indexが貼られていなくて(当たり前ですが)先日ひどい目にあいました。

# db/migrate/20181231235959_create_products.rb
class CreateProducts < ActiveRecord::Migration
  def change
    create_table :products do |t|
      # (略)
      t.integer :shop_id, unique: true
      # (略)
    end
  end
end

はい。 ActiveRecord::ConnectionAdapters::TableDefinitioncolumn メソッドの opthionsunique: true を渡してもunique indexは貼られませんね。

create_table でカラムを定義するのと同時に unique index を貼るやつの結論

上記のオプションの渡し方の正解は下記ですね。

t.integer :shop_id, index: { unique: true }

下記でいけるので。

t.type :column_name, index: { unique: true }

下記はだめ。

t.type :column_name, unique: true

そんなオプションの渡し方はない。

ActiveRecord::ConnectionAdapters::SchemaStatements の create_table メソッドのコードを読む

まあ、以上なのですが、せっかくなのでこの部分が実際にどういう実装になっているかを知るために ActiveRecord::ConnectionAdapters::SchemaStatementscreate_table メソッドについて Active Recordのコードを読んでみますか。

読んだ環境はRails 5.2.2 (GitHub - rails/rails at v5.2.2)です。

create_table

https://github.com/rails/rails/blob/v5.2.2/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb#L290

def create_table(table_name, comment: nil, **options)
  td = create_table_definition table_name, options[:temporary], options[:options], options[:as], comment: comment
  # (中略)
  yield td if block_given?
  # (中略)
end

yield に渡しているのは td なので、普段 t.integer みたいに書いている tcreate_table_definition で定義されているようだ。

create_table_definition table_name

https://github.com/rails/rails/blob/v5.2.2/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb#L1278

def create_table_definition(*args)
  TableDefinition.new(*args)
end

tdTableDefinitionインスタンスだった。

TableDefinition

https://github.com/rails/rails/blob/v5.2.2/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb#L257

class TableDefinition
  include ColumnMethods
  # (中略)
end

ColumnMethods をincludeしている。

ColumnMethods

https://github.com/rails/rails/blob/v5.2.2/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb#L201

module ColumnMethods
  # (中略)
  # Appends a column or columns of a specified type.
  #
  #  t.string(:goat)
  #  t.string(:goat, :sheep)
  #
  # See TableDefinition#column
  [
    :bigint,
    :binary,
    :boolean,
    :date,
    :datetime,
    :decimal,
    :float,
    :integer,
    :json,
    :string,
    :text,
    :time,
    :timestamp,
    :virtual,
  ].each do |column_type|
    module_eval <<-CODE, __FILE__, __LINE__ + 1
      def #{column_type}(*args, **options)
        args.each { |name| column(name, :#{column_type}, options) }
      end
    CODE
  end
  alias_method :numeric, :decimal
end

普段使っているシンタックスシュガーがメタプロで定義されている。
t.integer だと column(name, :integer, options) } なので、結局 column メソッドに options 引数が渡されて実行されている。

column

https://github.com/rails/rails/blob/v5.2.2/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb#L355

def column(name, type, options = {})
  name = name.to_s
  type = type.to_sym if type
  options = options.dup
  # (中略)
  index_options = options.delete(:index)
  index(name, index_options.is_a?(Hash) ? index_options : {}) if index_options
  @columns_hash[name] = new_column_definition(name, type, options)
  self
end

optionsindex キーの中身は index メソッドに引数として渡されている。

index

https://github.com/rails/rails/blob/v5.2.2/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb#L380

# Adds index options to the indexes hash, keyed by column name
# This is primarily used to track indexes that need to be created after the tab
#
#   index(:account_id, name: 'index_projects_on_account_id')
def index(column_name, options = {})
  indexes << [column_name, options]
end

コメントの通りですが、 indexesoptions を追加している。

create_tableに戻る

https://github.com/rails/rails/blob/v5.2.2/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb#L314

def create_table(table_name, comment: nil, **options)
  td = create_table_definition table_name, options[:temporary], options[:options], options[:as], comment: comment
  # (中略)
  unless supports_indexes_in_create?
    td.indexes.each do |column_name, index_options|
      add_index(table_name, column_name, index_options)
    end
  end
  # (中略)
end

td.indexes のそれぞれの要素の2つ目の index_optionsadd_index メソッドに index_options として渡している。
結局 add_index が呼ばれていますね。

--

ActiveRecordのコードはこれ以上追いませんが、結局

t.type :column_name, index: { key: value }

index 部分のオプションは

 add_index(table_name, column_name, {key: value})

のように、 add_index にオプションとして渡されるのでした。
そして add_index は unique indexを作成するときは オプションとして unique: true を渡す必要ので、 create_table の中の t.type でunique indexを貼りたいときはオプションとして index をキーにして、

t.type :column_name, index: { unique: true }

としないといけないのでした。

コードを読むのは疲れますが、実際の処理の中身がよく分かるのでこれからも適宜読んでいきたいですね。
こちらからは以上です。

補足

上記で見た、ActiveRecord::ConnectionAdapters::TableDefinitioncolumn メソッドのオプションでunique indexを貼る方法が以外の方法は下記がある。

ActiveRecord::ConnectionAdapters::SchemaStatementsadd_index メソッド

テーブル作成後にindexを追加するやつ。

create_table :products do |t|
  t.integer :shop_id
end

add_index :products, :shop_id, unique: true, name: 'products_shop_id_index'

ActiveRecord::ConnectionAdapters::TableDefinitionindex メソッド

create_tableの中でindexを追加するやつ。

create_table :products do |t|
  t.integer :shop_id  
   
  t.index :category_id, unique: true, name: 'products_shop_id_index'
end

ActiveRecord::ConnectionAdapters::Tableindex メソッド

change_tableでindexを追加するやつ。
create_table 内で利用する1つ上と形は一緒ですね。

change_table :products do |t|
  t.index :shop_id, unique: true, name: 'products_shop_id_index'
end

参考

小さく薄くrails newする

HerokuでRubyスクリプトのような何かを動かす時に、activerecord等は利用したいのでRuby単体ではなくてRailsとしての方が都合がいいのだけどViewやJS関係は特に必要ない。そんな時があります。そんな時がありますね。
そういうわけで、上記のような用途向けのView関連、JS関連のファイルの作成を全てスキップしたrails newのオプション構成を調べました。

確認環境は

です。

結論

結論は下記です。

rails new . --database=postgresql --skip-yarn --skip-git --skip-action-mailer --skip-active-storage --skip-action-cable --skip-sprockets --skip-javascript --skip-turbolinks --skip-test --api --skip-bundle

そして、下記は僕のコピペ用です。

docker-compose run --rm app rails new . --database=postgresql --skip-yarn --skip-git --skip-action-mailer --skip-active-storage --skip-action-cable --skip-sprockets --skip-javascript --skip-turbolinks --skip-test --api --skip-bundle

各オプションの解説

各オプションの概要は下記の通りです。

オプション 効果
--database=DATABASE 使用するdatabaseを指定
--skip-yarn Yarnを利用しない
--skip-git .gitignore を作成しない
--skip-action-mailer Action Mailer関連のファイルを作成しない
--skip-active-storage Active Storage関連のファイルを作成しない
--skip-action-cable Action Cable関連のファイルを作成しない
--skip-sprockets Sprockets関連のファイルを作成しない
--skip-javascript JavaScript関連のファイルを作成しない
--skip-turbolinks turbolinks gemを利用しない
--skip-test test関連ファイルを作成しない
--api APIとして利用するapp向けの小さい構成
--skip-bundle bundle installを実行しない

詳しくは

rails new --help

でどうぞ。

補足しておくと、

  • 予め .gitignore は用意しているので デフォルトの .gitignore は作成しない ( -skip-git )
  • どうせ後でRspecをインストールするので標準のtest関連ファイルは必要ない ( --skip-test )
  • どうせ後でGemfileに必要なgemを追加して bunde install するので bundle install しない ( --skip-bundle )

こちらからは以上です。

参考情報

参考までに、上記オプション付与時にremoveされるファイルは下記の通りです。
ちゃんとどのファイルが削除されるのか表示されて便利だ……。

rails new . --database=postgresql --skip-yarn --skip-git --skip-action-mailer --skip-action-cable --skip-sprockets --skip-javascript --skip-turbolinks --skip-test --api --skip-bundle
  (中略)
      remove  app/assets
      remove  lib/assets
      remove  tmp/cache/assets
      remove  app/helpers
      remove  test/helpers
      remove  app/views
      remove  public/404.html
      remove  public/422.html
      remove  public/500.html
      remove  public/apple-touch-icon-precomposed.png
      remove  public/apple-touch-icon.png
      remove  public/favicon.ico
      remove  app/assets/javascripts
      remove  config/initializers/assets.rb
      remove  app/views/layouts/mailer.html.erb
      remove  app/views/layouts/mailer.text.erb
      remove  app/mailers
      remove  test/mailers
      remove  app/assets/javascripts/cable.js
      remove  app/channels
      remove  config/initializers/cookies_serializer.rb
      remove  config/initializers/content_security_policy.rb
      remove  config/initializers/new_framework_defaults_5_2.rb
      remove  bin/yarn

また、作成されるGemfileとデフォルトのGemfile( Gemfile.default )の差分は下記となります。

デフォルトのGemfileと比べると、sass-rails , coffee-rails から始まって web-console , またtestの capybara , selenium-webdrive に至るまでViewに関するgem一式が削除されていることがわかりますね。
(はてなブログのdiffのhighlight見づらいな……)

git diff --no-index -U10 Gemfile.default Gemfile
diff --git a/Gemfile.default b/Gemfile
index e45e610..c5441b8 100644
--- a/Gemfile.default
+++ b/Gemfile
@@ -1,62 +1,40 @@
 source 'https://rubygems.org'
 git_source(:github) { |repo| "https://github.com/#{repo}.git" }

 ruby '2.6.0'

 # Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
 gem 'rails', '~> 5.2.2'
 # Use postgresql as the database for Active Record
 gem 'pg', '>= 0.18', '< 2.0'
 # Use Puma as the app server
 gem 'puma', '~> 3.11'
-# Use SCSS for stylesheets
-gem 'sass-rails', '~> 5.0'
-# Use Uglifier as compressor for JavaScript assets
-gem 'uglifier', '>= 1.3.0'
-# See https://github.com/rails/execjs#readme for more supported runtimes
-# gem 'mini_racer', platforms: :ruby
-
-# Use CoffeeScript for .coffee assets and views
-gem 'coffee-rails', '~> 4.2'
-# 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.5'
-# Use Redis adapter to run Action Cable in production
-# gem 'redis', '~> 4.0'
+# gem 'jbuilder', '~> 2.5'
 # Use ActiveModel has_secure_password
 # gem 'bcrypt', '~> 3.1.7'

-# Use ActiveStorage variant
-# gem 'mini_magick', '~> 4.8'
-
 # Use Capistrano for deployment
 # gem 'capistrano-rails', group: :development

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

+# Use Rack CORS for handling Cross-Origin Resource Sharing (CORS), making cross-origin AJAX possible
+# gem 'rack-cors'
+
 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]
 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.0.5', '< 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'
 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 chromedriver to run system tests with Chrome
-  gem 'chromedriver-helper'
-end

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

参考リンク

qiita.com

列車運行情報を取得するgemを作った

関東エリアの列車の運行情報を取得するgemを作りました。
標題は目的語が大きく、情報を取得できるのは関東エリアのみです(大事なことなので)

github.com traininfo_kanto | RubyGems.org | your community gem host

インストール

Gemfileに下記の行を追加して bundle install するか、

# Gemfile
gem 'traininfo_kanto'

あるいは、直接gemをインストールしてください。

gem install traininfo_kanto

使い方

# app.rb
require 'traininfo_kanto'

TraininfoKanto.get(['山手線', '京浜東北線'], url: true)

こんなコードを用意しておいて

$ ruby app.rb

実行すると

["山手線は平常運転です。", "京浜東北線は列車遅延があります。\n宇都宮線内でドア点検を行った影響で、一部列車に遅れが出ています。 (9月21日 16時45分掲載)\nhttps://transit.yahoo.co.jp/traininfo/detail/22/0/"]

が返ってきます。

url オプションがあり、 true にすると「平常運転」以外の場合に最後に詳細ページのリンクを追加します(デフォルトは false )

コマンド

CLIも用意しているので、コマンドライン上で

$ traininfo_kanto get 山手線 京浜東北線
山手線は平常運転です。
京浜東北線は列車遅延があります。
宇都宮線内でドア点検を行った影響で、一部列車に遅れが出ています。 (9月21日 16時45分掲載)

みたいな感じでも使えます。 CLIでは url オプションはなしです(そのうち実装するかも)

Docker

shifumin/traininfo_kanto - Docker Hub

Docker Hubにイメージを上げたので、Dockerの実行環境があれば

$ docker run --rm shifumin/traininfo_kanto get 山手線 京浜東北線

で試すことができます。
Rubyの環境すら必要ありません。
便利ですね。

どうやって情報を取得しているか

単純にYahoo!路線情報の運行ページ{:target='_blank'}をnokogiriでスクレイピングして情報を取得しています。

やりたかったこと

要はこういうことをやりたかったんですよね。

f:id:shifumin:20181010200426p:plain

上記はうちのSlack botです。
で、運行情報取得部分だけgemに切り出しました。

注意点

さて、注意点があります。
関東の運行情報と言いながら現時点(2018年10月9日)でサポートしている路線は下記だけです。
(メトロ、都営とJRの僕が利用しそうな路線と後小湊鉄道だけ)
JRは頑張って後で追加します。私鉄は……w

Supported routes

まだできていないこと

  • サポートしている路線の数が少ない
    • せめてJR東日本(関東)の路線は全てカバーしたい
  • テストを全く書いていない
    • gemのテストの知見を得たいので書きたい
  • CLItraininfo_kanto list (運行情報を取得できる路線一覧を取得する)のサブコマンドくらいは流石に実装したい
    • 実装します

終わりに

Rubygems作成とDockerHubにDocker imageアップロードは初めてでしたが、学びがありよかったです。
こちらからは以上です。

第2回 DockerとCircleCIを使ってRailsアプリの自動テスト&自動Herokuへのデプロイ環境を作っていく

前回の記事の続きです。

前回の記事

shifumin.hatenablog.com

前回は作成したテンプレートの紹介でした。 今回は前回の記事で作成したテンプレートを使って実際にRailsアプリ開発環境を作っていきます。
タイトルは紛らわしいのであとで変更するかもしれません。

使用するテンプレートは下記のリポジトリです。

github.com

動かし方

最低限のそれはGitHubのREADMEに書いていますので、そちらも見てください。
https://github.com/shifumin/docker_rails_heroku

必要な環境

  • Gitのインストール, GitHubのアカウント, Herokuのアカウント作成辺りは前提として
  • Docker
    • Macなら "Docker for Mac"をインストールが楽
      • 詳しくは下記を参照のこと

www.docker.com

  • Heroku CLI
    • Ma(ry Homebrewで brew install heroku が楽

構成

2018年7月7日現在は下記の構成のRailsアプリとなっています。

準備

docker(docker-compose)コマンドの実行はDockerを起動させておく必要があるので起動させてから実行していきます。

まず、必要なDocker imageをpullします。

docker pull ruby:2.5.1
docker pull postgres:10.4

次にリポジトリをcloneします。

git clone git@github.com:shifumin/docker_rails_heroku
cd docker_rails_heroku

rails newしてアプリを作成していきます。

docker-compose run --rm web rails new . --database=postgresql --skip-bundle --skip-test

Overwrite /rails_app/README.md? (enter "h" for help) [Ynaqdh] Y
Overwrite /rails_app/.gitignore? (enter "h" for help) [Ynaqdh] n
Overwrite /rails_app/Gemfile? (enter "h" for help) [Ynaqdh] Y

README.mdは上書きしてもしなくても、.gitignoreは上書きせずにGemfileは上書きします(Y n Y)

database.yml.sampleをコピーしてconfig/database.ymlを置き換えます。
Gemfileにお好みで追加のgemを加えます。

cp -i database.yml.sample config/database.yml
overwrite config/database.yml? (y/n [n]) y

vim Gemfile

僕は今RailsでLine botを作成しているので下記を追加しました。

# adding
group :development, :test do
  gem 'factory_bot_rails'
  gem 'rspec-rails'
end

group :development do
  gem 'awesome_print'
  gem 'pry-byebug'
  gem 'pry-doc'
  gem 'pry-rails'
  gem 'pry-stack_explorer'
  gem 'refe2'
  gem 'tapp'
end

Gemfileにgemを追加したら、bundle installします。
tzinfo-data はウィンドーズ以外は必要なさそうなので削除しました(下記の2行)

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

参考

github.com

余談ですが、僕の場合はbot開発なのでview関係のgem(sass-rails等)は必要ないけど、そのまま残しているのでそれらもきちんと整理する必要がありますね。

ここらでdocker-compose buildします(buildの時に合わせてbundle installもするし、上のbundle installはいらない気がしてきた!)

docker-compose build

一応、RSpecのインストールも済ませておきます(面倒がらずに個人開発でもテストもきちんと書いていくぞというやっていく気持ちの現れ)
また、rails db:createも行います。

docker-compose run --rm web rails g rspec:install
docker-compose run --rm web rails db:create

ここまでの前準備でローカル開発環境ではRailsアプリを動かす準備ができたのでdocker-compose upで起動させてブラウザで確認します。

docker-compose up

http://localhost:3000/ で確認。

f:id:shifumin:20180708113319p:plain

確認できました。

終わりに

今回はDockerized Railsアプリのテンプレートでローカル開発環境でRailsアプリを起動させるところまで進めました。
次回はCircleCIの設定を進めていきたいと思います(予定)

参考

shifumin/docker_rails_heroku: Template for building Rails application environment with Docker on Heroku.
https://github.com/shifumin/docker_rails_heroku

Docker For Mac | Docker
https://www.docker.com/docker-mac

library/ruby - Docker Hub
https://hub.docker.com/_/ruby/

library/postgres - Docker Hub
https://hub.docker.com/_/postgres/

"The dependency tzinfo-data will be unused" on Ubuntu · Issue #12 · tzinfo/tzinfo-data
https://github.com/tzinfo/tzinfo-data/issues/12

Heroku+RailsでLine botを作る 第11回 ローカル開発環境でのデバッグ

前回までのあらすじ

第1回: http://shifumin.hatenablog.com/entry/2018/04/20/000500
第2回: http://shifumin.hatenablog.com/entry/2018/04/22/002000
第3回: http://shifumin.hatenablog.com/entry/2018/04/23/000500
第4回: http://shifumin.hatenablog.com/entry/2018/04/24/000500
第5回: http://shifumin.hatenablog.com/entry/2018/04/25/000500
第6回: http://shifumin.hatenablog.com/entry/2018/04/26/000500
第7回: http://shifumin.hatenablog.com/entry/2018/04/27/000500
第8回: http://shifumin.hatenablog.com/entry/2018/04/29/000500
第9回: http://shifumin.hatenablog.com/entry/2018/04/30/002000
第10回: shifumin.hatenablog.com

公式サンプルコードのオウム返しbotRailsへ実装(移植)し、Herokuへとデプロイし動作することを確認しました(第一部完)
また、ローカル開発環境での動作確認の方法を調べました。
今回は前回の続きでローカル開発環境でのデバッグの方法を見ていきたいと思います。

Gemの追加

Gemfile

まずデバッグ用のGemを追加しましょう。
追加するのは下記の5つです。
Railsデバッグ用gemは他にもありますがLINE bot開発で使えそうなのはこれらくらいでしょうか。
Gemfileの最後に group :development で追加しましょう。

(前略)
group :development do
  gem 'awesome_print'
  gem 'pry-byebug'
  gem 'pry-rails'
  gem 'pry-stack_explorer'
  gem 'tapp'
end

Gemfileにgemを追加したらそれらのgemをインストールしましょう。

bundle install

gemの詳細はそれぞれのGitHubリポジトリに任せます。

github.com github.com github.com github.com github.com

以下、各Gemの一言解説です。

awesome_print

デバッグ時にオブジェクトのデータ構造を綺麗に表示してくれる。

pry-byebug

プログラムの好きなところでピンポイントで止めてステップ実行でデバッグできる。

pry-rails

Rails Consoleでpryを使えるようにする。

pry-stack_explorer

デバッグ中にコールスタックを移動できる。

tapp

レシーバを pp しながら返す tapp を使うことができる。

僕が大昔に書いたQiitaの記事も合わせてどうぞ(同じことしか書いていませんが)

qiita.com

実際のデバッグ

実際にデバッグを行なっていきましょう。
コードのデバッグしたい位置に binding.pry を埋め込みます。
この binding.pry を埋め込んだ位置がブレークポイントとなり、プログラムが実行された時にその箇所で一旦停止します。

app/controllers/webhook_controller.rb の16行目(下記の3行目)に binding.pry を埋め込みました。
ループの中の event の中身がどうなっているかを確認するためです。

     events = client.parse_events_from(body)
     events.each { |event|
       binding.pry # ここに埋め込んだぞo(^-^)o
       case event
       when Line::Bot::Event::Message
         case event.type
         when Line::Bot::Event::MessageType::Text
           message = {
             type: 'text',
             text: event.message['text']
           }
           client.reply_message(event['replyToken'], message)
         when Line::Bot::Event::MessageType::Image
         end
       end
     }

     head :ok
   end

ブレークポイントを埋め込んで、Railsアプリ(bot)とngrokを起動させて動作確認をできる状態で、botに例えば「テスト」とメッセージを送ってみます。

Frame number: 0/69

From: /path/to/app/controllers/webhook_controller.rb @ line 17 WebhookController#callback:

     6: def callback
     7:   body = request.body.read
     8:
     9:   signature = request.env['HTTP_X_LINE_SIGNATURE']
    10:   unless client.validate_signature(body, signature)
    11:     error 400 do 'Bad Request' end
    12:   end
    13:
    14:   events = client.parse_events_from(body)
    15:   events.each { |event|
    16:     binding.pry
 => 17:     case event
    18:     when Line::Bot::Event::Message
    19:       case event.type
    20:       when Line::Bot::Event::MessageType::Text
    21:         message = {
    22:           type: 'text',
    23:           text: event.message['text']
    24:         }
    25:         client.reply_message(event['replyToken'], message)
    26:       when Line::Bot::Event::MessageType::Image
    27:       end
    28:     end
    29:   }
    30:
    31:   head :ok
    32: end

5.2.0@2.5.1 (#<WebhookController:0x00007f85fbe503e8>)>

概ね、上記のようになると思います。
メッセージを送ったので WebhookController#callback が呼ばれて、16行目に binding.pry を埋め込んだので、直後の17行目でプログラムが一時停止しています。
ここで、変数 event の情報を知りたいので、event を調べてみます。

5.2.0@2.5.1 (#<WebhookController:0x00007f85fbe503e8>)> event
#<Line::Bot::Event::Message:0x00007f85fbdb1e28 @src={"type"=>"message", "replyToken"=>"1bccd66640cf4b8098ca2422785998f5", "source
=>{"userId"=>"Ue9cef635faec1f63eeed49f190d3baad", "type"=>"user"}, "timestamp"=>1530371340095, "message"=>{"type"=>"text", "id"=>
8193953148916", "text"=>"テスト"}}>、

変数 event の情報が分かりました。
また eventtype を調べると下記のようになります。

5.2.0@2.5.1 (#<WebhookController:0x00007f85fbe503e8>)> event.type
"text"

この event はスタンプを送った場合は下記のように

5.2.0@2.5.1 (#<WebhookController:0x00007f85f91169e0>)> event
#<Line::Bot::Event::Message:0x00007f85fbe28820 @src={"type"=>"message", "replyToken"=>"6dddf3aa4cd14fbeadbfcf9b3d0633ca", "source"=>{"userId"=>"Ue9cef635faec1f63eeed49f190d3baad", "type"=>"user"}, "timestamp"=>1530372349972, "message"=>{"type"=>"sticker", "id"=>"8194033895954", "stickerId"=>"2591647", "packageId"=>"1062629"}}>

5.2.0@2.5.1 (#<WebhookController:0x00007f85fbcc80e8>)> event.type
"sticker"

また、画像を送った場合は下記のようになります。

5.2.0@2.5.1 (#<WebhookController:0x00007f85f8eeba08>)> event
#<Line::Bot::Event::Message:0x00007f85fda81ee0 @src={"type"=>"message", "replyToken"=>"6ef3f17524004110a3cf9aada9bcb992", "source"=>{"userId"=>"Ue9cef635faec1f63eeed49f190d3baad", "type"=>"user"}, "timestamp"=>1530372296992, "message"=>{"type"=>"image", "id"=>"8194029881000"}}>

5.2.0@2.5.1 (#<WebhookController:0x00007f85f8eeba08>)> event.type
"image"

下記は、ブレークポイントで停止させた後に実際に変数を調べている様子です。

Image from Gyazo

また、下記はステップ実行で1行ずつプログラムを進めている様子です。

Image from Gyazo

ステップ実行では、下記のコマンド等でプログラムを進めることができます。

next: 次の行に進める
step : メソッドの内部に入る
finish: 現在のメソッドを抜ける
continue: デバッグを終了する(プログラムの実行をconfinueする)
whereami: 表示を現在のスコープのソースコードに戻す

実際は whereaminext を一々タイプするのは面倒なのでもちろんエイリアスを設定しているのですが、きちんと?正式コマンドをタイプしています、これはデモなので。。。
そのうち、pryの設定ファイルである .pryrc の記事を書くかもしれません。

pryを使ったデバッグについて詳しいことは下記の記事をご参照ください(投げ)
他にも色々とコマンドがあります。

qiita.com

qiita.com

終わりに

今回はローカル環境でのデバッグを見ていきました。
次回ではLINE bot開発に戻り、機能追加を進めていきましょう。

参考

awesome-print/awesome_print: Pretty print your Ruby objects with style -- in full color and with proper indentation
https://github.com/awesome-print/awesome_print

deivid-rodriguez/pry-byebug: Step-by-step debugging and stack navigation in Pry
https://github.com/deivid-rodriguez/pry-byebug

rweng/pry-rails: Rails >= 3 pry initializer
https://github.com/rweng/pry-rails

pry/pry-stack_explorer: Walk the stack in a Pry session
https://github.com/pry/pry-stack_explorer

esminc/tapp: tap { pp self }
https://github.com/esminc/tapp

Rubyプロジェクトの始め方の備忘録 - Qiita
https://qiita.com/shifumin/items/e065dd5b35bc22497037

今更聞けないpryの使い方と便利プラグイン集 - Qiita https://qiita.com/k0kubun/items/b118e9ccaef8707c4d9f

pry-byebugでrubyデバッグする - Qiita
https://qiita.com/AknYk416/items/6f0bec58712edaf4940e