Quantcast
Channel: Ruby on Railsの記事一覧|TechRacho by BPS株式会社
Viewing all 1381 articles
Browse latest View live

Rails: SendGrid(Web API)とAction Mailerでメールを送信する

$
0
0

こんにちは、yamadaです。入社から1年半ほど経ちますが、初めての記事になります。
今回はRailsとSendGridでメール送信を行った時の手順をまとめようと思います。

  • Rails 5.2
  • SendGrid(V3 Mail Send API)


sendgrid.comより

APIキーの作成

まずはSendGridのAPIキーを作成します。作成方法は公式ドキュメントをご確認ください。

権限は使用目的に合わせて設定しましょう。単純なメール送信だけであれば「Mail Send」を「Full Access」にすればOKです。


APIキーを管理する - ドキュメント | SendGridより

実装

配信方法の切り替えを簡単にするため、delivery_methodを追加する形で対応しました。

参考: Action Mailerのdelivery_methodに独自の配信方法を追加する

SendGridのAPIについては公式ドキュメント(jp, en)をご確認ください。

API呼び出しは公式のgemを利用しています。

実際に作成したのは以下のようなコードになります。
プロジェクトの中に追加する形で対応しましたが、gemとして作成するとより良いかもしれませんね。

# config/initializers/sendgrid.rb
ActionMailer::Base.add_delivery_method :sendgrid, Mail::SendGrid,
  api_key: xxxxxxxxxxxxxxxxxxxxxxxxxxx
# config/environments/development.rb
config.action_mailer.delivery_method = :sendgrid
# lib/mail/send_grid.rb
class Mail::SendGrid
  def initialize(settings)
    @settings = settings
  end

  def deliver!(mail)
    from = SendGrid::Email.new(email: mail.from.first)
    to = SendGrid::Email.new(email: mail.to.first)
    subject = mail.subject
    content = SendGrid::Content.new(type: 'text/plain', value: mail.body.raw_source)
    sg_mail = SendGrid::Mail.new(from, subject, to, content)

    sg = SendGrid::API.new(api_key: @settings[:api_key])
    response = sg.client.mail._('send').post(request_body: sg_mail.to_json)
    puts response.status_code
  end
end

メール送信

Mailerを用意してメールを送信してみます。

# app/mailers/test_mailer.rb
class TestMailer < ApplicationMailer
  def test_mail
    mail(from: 'from@example.com', to: 'to@example.com', subject: 'テストメール')     
  end
end
# app/views/test_mailer/test_mail.text.erb
SendGrid送信テスト
TestMailer.test_mail.deliver_now

最後に

この記事ではWeb APIを使用したメール送信について書きましたが、SMTPを使用することもできます。
両者の違いついてはSendGridとの連携をご確認ください。

Web APIを使用する場合、送信したメールとEvent Webhookの連携が簡単になるのかなと思います。
RailsでEvent Webhookを利用した時の話も機会があればまとめようと思います。

関連記事

SendGridで独自ドメインからdocomo宛に送信するときの注意点


RubyMineからGitが使えて便利

$
0
0

BPSの福岡拠点として一緒にお仕事させて頂いてます、株式会社ウイングドアの馬場です。

みなさま、RubyMineはお使いでしょうか?

RubyMineとは?

JetBrains社というチェコ共和国に本社を置くソフトウエア会社が開発した、
Ruby on Rails開発用のIDE(統合開発環境)です。

JetBrains社製の他のプロダクト(PHPStormWebStormIDEAなど)でも、今回ご紹介する機能は使えますので、
参考にしていただけるとうれしいです。


jetbrains.comより

有料エディタですが、30日無料の試用版があります。
また、学生は無料で、日本でも ac.jp ドメインを持っていれば使うことができます。
(授業でWebStormを使っている大学もあるそうです✨

RubyMine、とてもおすすめなので、触ったことがない方はぜひ試してみてほしいです!

RubyMineはたいへん機能が充実しています。
一部の機能しか使いこなせていませんが、最近便利だなーと思っている機能をご紹介します。

RubyMineでGitを使う

いつのコミットか知りたいとき

ここっていつからこうなっているんだろう?
何か理由があってこうなっているんだろうけど、誰に聞いたらいいのかな?
と疑問が湧いた時、どうしていますか?

GitHubに探しに行く? git blame コマンドで、そのファイルをいつ、誰が編集したのか見る?
でも、今開いてるファイル上でさっと確認できたらいいと思いませんか。

やってみましょう!

RubyMineで、行番号を右クリック。

そして、Annotateをクリック。
するとその行の修正日と修正者が表示されます。

行をホバーすると、コミット識別番号やコミットメッセージまで表示してくれます。

そして、行番号をクリックするとそのコミットで修正したファイル群が表示されます。

さらに、そのファイルをクリックすると、差分が見ることができるのです。

(加工のせいでちょっと見辛くなってしまいましたが)差分箇所だけでなく、全体が表示されています。
全体を表示してくれるので、前後のコードを把握することができます。

Annotateされている箇所を右クリックして、Close Annotationsを押すとAnnotationが非表示に戻ります。

masterとの差分を知りたいとき

開発を進めていて、元のファイルがどうなっていたか確認したいと思った時、
そんな時も便利な機能があります。

ファイルの適当なところで右クリックします。
なんかGitっていうのがありますね。

ここでCompare with Branchを選択してみます。

masterと比べてみます。

masterとの差分が分かりやすい。

しかも>>をクリックすると、差分を流し込むこともできます。

おわりに

コードを書きながら簡単に差分が確認できるので、楽!
もっといろいろな機能を使いこなせるよう、これからもRubyMineと仲良くしていこうと思います。


株式会社ウイングドアでは、Ruby on RailsやPHPを活用したwebサービス、webサイト制作を中心に、
スマホアプリや業務系システムなど様々なシステム開発を承っています。

Rails: 昔プログラミングの勉強の一環で作成したウェブアプリを振り返ったりツッコミを入れてみる

$
0
0

こんにちは。今年もアドベントカレンダーの時期がやってきました。
いきなりですが、こちらを御覧ください。
https://triplaning.herokuapp.com/

これは昔、私がプログラミングを勉強し始めの頃に、勉強の一環としてなにかウェブアプリを作ってみたいと思い、Ruby on Railsを使って作成したウェブアプリです。

今回、この作成したアプリに対して、振り返り&いろいろツッコミをいれていこうと思います。

はじめに

まずはじめにこのアプリがどんなアプリなのかを一応説明すると、旅行のプラン(行程表)を作成するアプリです。
(私が旅行が好きなので、旅行に関するアプリだと作るモチベーションがより湧くかなと思い、このテーマにしたような記憶があります。)

サイトのトップページのGIF画像にあるように、行きたい場所を入力していき、各場所を回る順序を入れると、Google Maps API の中のAPI(だった記憶)を使って2地点の距離や移動にかかる時間やなどを算出した結果を表示します。その後、すべての入力が終わると、作成した工程表をPDFとして出力したり、メンバーにメールで共有する機能などがあります。

※ 実際に使用される想定でこのアプリを作ってはいないので、もし旅行の行程表を作成したいという人がいれば、他にもサービスはたくさんありますのでそちらをご利用ください。

また、あくまでプログラミングを始めたばかりの私が、勉強目的で使って作ってみたというものなので、
そのあたりを踏まえて上で挙げたウェブアプリも本記事もネタとして見ていただければと思います。

では早速、みていきます。

まずは見た目

まず見た目の部分ですが、トップ画面を見た感じだと、横並びの項目の高さのずれや、中央にあるべきものがずれていたりなどいろいろありそうです。

おそらく見た目でいうとトップ画面は一番こだわって作った思うので、当時の精一杯でCSSを書いてはいたことでしょう。(気合は伝わってきました…)

粗を探すとキリがなさそうなので見た目はこれくらいにします。

操作もしてみる

いくつか操作もしてみようと思います。

まずはフェイスブックで新規登録/ログインを試してみましたが、これはエラーとなり登録及びログインができませんでした。おそらくですが、FacebookのAPIは頻繁にアップデートが行われている印象があるので、そこでおいていかれてしまった感じでしょうか。

また、ログインすると、プランに対して場所を紐付け登録できるはずなのですが、こちらの登録もできなくなってました。おそらく、何かしらAPIを使って地名から緯度経度を取得するようにした記憶があるので、↑と同じような理由ではないかと想像しています。

せっかくなのでコードもちらっと見てみる

ということで、まずはモデルのコードを見てみました。

ぱっと目に飛び込んできたのがこのコードです。
ActiveRecord::Baseを継承したモデルの中のインスタンスメソッドです。

# 行きたい度に応じて、★を表示させる。
# とりあえず、場所にひもづくピンのなかで、一番目の情報を表示する。
def show_star
  if self.pins[0].present?
    if self.pins[0].want == 0
      "★"
    elsif self.pins[0].want == 1
      "★★"
    else
      "★★★"
    end
  end
end

優先度に応じてそれを★の数で表現したかったのだろうなーと思います。

  • selfは不要なのでは?
  • 対象がすべてself.pins[0].wantなのでcasewhenを使ったほうが見やすくなりそう?
  • メソッド名のshow_starという命名が斬新で面白い(わかりにくい)
  • そもそも表示に関するメソッドなので、モデルに書かずに、Decoratorなどを導入してそちらに書いたほうが良さそう?

たった数行のコードですが、他にもいろいろ突っ込みどころはありそうです。

思いのほかモデルにコード自体が少なかったので、コントローラも見てみました。

まずはコントローラ内のpublicなメソッドです。場所の情報を削除する処理かと思います。

def destroy
  plan_id = Place.find(params[:id]).plan.id
  @place.destroy
  @reorder = Place.where(plan_id: plan_id)
  @reorder.each_with_index {|route, i| Place.update(route, {:route => i + 1})}
  respond_to do |format|
    format.html { redirect_to plan_path(plan_id), notice: '削除しました' }
    format.json { head :no_content }
  end
end

削除した後に、route(並び替えの順番)を1つずつ更新しているようですが、
これらの処理はセットなのでトランザクションで囲ってあげたほうが良さそうに見えました。

続いてもpublicなメソッドです。

def pdf
  respond_to do |format|
    format.html { redirect_to :action => 'pdf', :format => 'pdf', debug: 1 }
    format.pdf do
      @plan = Plan.find(params[:id])
      @places = @plan.places.order(:route)
      render pdf: 'pdf',
        layout: 'pdf_layout.html.erb',
        encoding: 'UTF-8',
        no_background: false
    end
  end
end

pdfというアクションで工程表をPDFとして出力する処理だと思いますが、
要求されたフォーマットがpdfであればpdf、htmlであればhtmlで出力するのがいいと思うので、pdfというアクションはやめたほうが良さそうと思いました。あとよく見てみるとhtmlの要求をpdfの要求としてリダイレクトさせてますね…

続いてはメール送信の処理です。(あるコントローラ内のpublicのメソッドからの抜粋)

# deliverメソッドを使って、メールを送信する。メンバー全員に送信する
members.each do |member|
  MemberMailer.plan_finish_email(@plan, member).deliver
end

一人ひとりそれぞれにメール送信するならば、直ちに送信するのではなく、非同期にした方が良いかもと思いました。

最後にビューも見てみようと思いファイルを開いてみたところ、インデントずれなど多く見る気が削がれたのでそっとファイルを閉じました…

まとめ

  • プログラミングを始めたばかりの頃に作ったアプリを見たり、書いたコードを見てみて、すごく懐かしい気分になりました。
  • 予想通りではありましたがツッコミどころの多いコードでした。
  • レベルはともわれ昔の自分にツッコミを入れられたので、少しでも自分の成長を実感できたのはよかったです。
  • 気分が乗れば年末年始の時間がある時にいろいろリファクタリングしてみたいと思いました。

おたより発掘

Rails: Herokuへのデプロイ時にwebpackでビルドしようとしてハマったこと

$
0
0

anekawaです。
ちょっと前に個人で開発したrailsアプリをHerokuにデプロイしようとしたときにハマったことを書いておこうと思います。

前提

  1. babelautoprefixerを使いたかったのでフロント周りはwebpackを使う
  2. Herokuへデプロイした際にwebpackによるビルドを走らせたかった
  3. 開発環境ではrootディレクトリ直下にfrontend/というディレクトリを作り、その中にフロントエンド関係のソースを詰めていた(package.json/node_modulesを含む)
  4. frontend/ディレクトリ内でwebpackによるビルドをしていた
  5. turbolinkssprocketsは使っておらず、webpackerも使っていない
  6. パッケージマネージャはyarnを使用
  7. 開発時のディレクトリ構成は以下
rails_root
├── appなどrailsディレクトリ達
└── frontend
    ├── package.json
    ├── scripts
    │   └── index.js      // エントリーポイント
    ├── webpack.config.js
    └── yarn.lock

ハマったこと

前提3で書いてあるようにして開発を進めていたため、開発環境では

cd frontend
yarn run webpack --config ./webpack.config.js --mode development

という感じでwebpackのビルドを行っていました(厳密にはwebpack-dev-serverを使用)。
公式によれば、yarnの導入にはpackage.jsonの中に

"engines": {
  "yarn": "1.x"
}

を記述しないといけないみたいなので、これとビルド用npm scriptsである

  "scripts": {
    "postinstall": "cd ./frontend && yarn && yarn run webpack --config ./webpack.config.js --mode production"
  },

を、frontend/直下のpackage.jsonに書いていざデプロイすると、

$ git push heroku master

Enumerating objects: 6484, done.

Counting objects: 100% (6484/6484), done.
Delta compression using up to 2 threads.
Compressing objects: 100% (5289/5289), done.
Writing objects: 100% (6484/6484), 31.05 MiB | 1.55 MiB/s, done.
Total 6484 (delta 840), reused 6454 (delta 818)
remote: Compressing source files... done.
remote: Building source:
remote:
remote: -----> Node.js app detected
remote:
remote: -----> Creating runtime environment
remote:
remote:        NPM_CONFIG_LOGLEVEL=error
remote:        NODE_ENV=production
remote:        NODE_MODULES_CACHE=true
remote:        NODE_VERBOSE=false
remote:
remote: -----> Installing binaries
remote:        engines.node (package.json):  8.11.3
remote:        engines.npm (package.json):   unspecified (use default)
remote:
remote:        Resolving node version 8.11.3...
remote:        Downloading and installing node 8.11.3...
remote:        Using default npm version: 5.6.0
remote:        Resolving yarn version 1.x...
remote:        Downloading and installing yarn (1.13.0)...
remote:        Installed yarn 1.13.0
remote:
remote: -----> Building dependencies
remote:        Installing node modules (package.json)
remote:        up to date in 0.075s
remote:
remote: -----> Caching build
remote:        - node_modules (nothing to cache)
remote:
remote: -----> Pruning devDependencies
remote:        Skipping because npm 5.6.0 sometimes fails when running 'npm prune' due to a known issue
remote:        https://github.com/npm/npm/issues/19356
remote:
remote:        You can silence this warning by updating to at least npm 5.7.1 in your package.json
remote:        https://devcenter.heroku.com/articles/nodejs-support#specifying-an-npm-version
remote:
remote: -----> Build succeeded!
remote:  !     This app may not specify any way to start a node process
remote:        https://devcenter.heroku.com/articles/nodejs-support#default-web-process-type
remote:
remote: -----> Ruby app detected
remote: -----> Compiling Ruby/Rails
remote: -----> Using Ruby version: ruby-2.5.1
remote: -----> Installing dependencies using bundler 1.15.2
remote:        Running: bundle install --without development:test --path vendor/bundle --binstubs vendor/bundle/bin -j4 --deployment

# (gemのインストール等なので割愛) 

remote:        Warning: the running version of Bundler (1.15.2) is older than the version that created the lockfile (1.16.3). We suggest you upgrade to the latest version of Bundle
r by running `gem install bundler`.
remote: -----> Compressing...
remote:        Done: 51.3M
remote: -----> Launching...
remote:        Released v6
remote:        https://XXXXXX.herokuapp.com/ deployed to Heroku
remote:
remote: Verifying deploy... done.
To https://git.heroku.com/XXXXXX.git
 * [new branch]      master -> master

というログが出ました(開発環境をこの記事を書くために再現した環境でやっているので実際のログとはちょっと違うかも)。
一見成功しているかと思ったのですが、よく読むとnpm scriptsに指定したデプロイ時のビルドコマンドが一ミリも動いてないです。
もっと言うと、そもそもyarnのパッケージインストールも動いていません。

原因

色々触ってるうちに気づいたのですが、package.jsonの場所がルート直下でなかったのが原因だったようです。
フロントエンド周りで使うものをfrontend/ディレクトリに詰め込んだのが原因だったっぽいですね。
はじめはかなりググったりしたんですが、気づいてしまったらかなり単純なことでした…

対策

はじめはfrontend/内のpackage.jsonを適宜書き換えてルート直下に移してしまおうかと思ったのですが、それだと/node_modulesがルート直下に作られてしまいます。
rails関係のファイルとフロント周りのファイル分けたかったので以下のようにしました。

  1. ルートディレクトリ直下にはyarnのインストールとデプロイ時のビルドを実行するためのスクリプトを書いただけのpackage.jsonを配置
  2. frontend/には、具体的に使用するパッケージを載せたpackage.jsonを配置

1. ルートディレクトリ直下にはyarnのインストールとデプロイ時のビルドを実行するためのスクリプトを書いただけのpackage.jsonを配置

Herokuがデフォルトで認識するpackage.jsonはルートディレクトリ直下のもののようで、ここに先述した

"engines": {
  "yarn": "1.x"
}

を書いておきます。
また、具体的なwebpackによるビルドコマンドは先述したものを少し変えて、

"scripts": {
  "postinstall": "cd ./frontend && yarn && yarn run webpack --config ./webpack.config.js --mode production"
},

としています。
こうすれば、frontend/内でyarn installが走るため、/node_modulesがルートディレクトリ直下に作成されることを避けられます。

2. frontend/には、具体的に使用するパッケージを載せたpackage.jsonを配置

フロント周りのソースを詰めたfrontend/ディレクトリ内に配置するpackage.jsonには、ビルドのために使う諸々のパッケージを書きます(yarn add hogeで書き込まれるあれ)。

以上のことをすれば大丈夫でした。
あとはgit push heroku masterでいけるはずです。

おわりに

Herokuを使ったのは久々でしたが、ちょこっと作ったものをさっとデプロイするのにはやはり便利でした。
今回の開発でどうせならと思ってホビープランにも入ったので、来年はもっと個人開発をしていこうかなと思います。

Rails: Timecopを使わなくても時間を止められた話

$
0
0

Timecopを使わなくても時間を止められた話

テストで時間を止めたい時、Timecopを使わないと止められないと思っていましたが、Rails4.1以降は標準のActiveSupport::Testing::TimeHelpersでも止められると最近知りました。

1. Timecopを使った場合

  • 1ヶ月前の時刻で固定する。
Timecop.freeze(Time.current.prev_month)

2. ActiveSupport::Testing::TimeHelpersを使った場合

1ヶ月前の時刻で固定する。

travel_to(Time.current.prev_month)

元の時刻に戻す

travel_back

現在時刻で固定する

freeze_time

おたより発掘

週刊Railsウォッチ(20181225)Rails 6新機能第2弾「Action Mailbox」、url_forは慣れが要る、Ruby製サーバーレスフレームワークJetsほか

$
0
0

こんにちは、hachi8833です。12月早々からクリスマス気分が年末気分に追い越されてしまった心持ちです。

  • 各記事冒頭には⚓でパーマリンクを置いてあります: 社内やTwitterでの議論などにどうぞ
  • 「つっつきボイス」はRailsウォッチ公開前ドラフトを社内有志でつっついたときの会話の再構成です👄
  • 毎月第一木曜日に「公開つっつき会」を開催しています: お気軽にご応募ください

2018年最後のウォッチ、いってみましょう。

⚓新年: 週刊Railsウォッチ「公開つっつき会#6」開催のお知らせ

次回の新春公開つっつき会は1/10(木)に開催いたします。皆さまのお気軽なご応募をお待ちしております🙇

⚓臨時ニュース: Ruby 2.6リリース㊗🎉🎉

⚓Rails: 先週の改修(Rails公式ニュースより)

⚓新機能: Action Mailbox

Action Textに続くRails 6の目玉機能になるそうです。やはりBasecamp内でドッグフーディングしていたのが切り出されたとのこと。

今回Railsコアチームに加わったGeorge Claghorn氏が、Active Storageとともにこれまで手がけてきたそうです。


つっつきボイス:「先週のつっつきの後で発表されてました」「ほっほ〜?」「ちょうどWebチームの方がアドベントで書いてくださったAction MailとSendGridでの送信記事↓のあたりをカバーしている感じかな、と思ったらMailboxなので受け取る方だった💦」「なるほど、受信メールをモデルに保存して、そういうあたりを共通化する感じ!😃1通受け取るだけならともかく、結構な数のメールを受け取るなら運用していると欲しいときありそう」「やりたいことはワカル: これより小規模なものは前からあった気もするけど、コントローラっぽく処理できるようになったのが新しいのかも」

Rails: SendGrid(Web API)とAction Mailerでメールを送信する

「あー、受信メールをAWS SESにも保存できるというのは確かにメリット: SESってメールをS3に保存するというオプションがあって、たぶんこのAction Mailboxを使うとSESに置いたメールを探したり取ってきたりできそう」「おー」「メールの自動応答的な機能に便利かも?: たとえばRedmineだと、コメントにメールで返信するとチケットのコメントに取り込んでくれる機能がありますね」「なるほど!」


redmine.jpより

「ところで、メールを受け取ってそれで何かをするみたいな仕組みって、現代ではかなり減ってきていますけどね☺」「あーこの間のウォッチで話題に出たAS/400の話みたいな😆」「メールを使いたいひとたちはまだまだたくさんいらっしゃるので☺

InboundEmailというシンプルなモデルが足されるそうです。

# 同記事より
class ActionMailbox::InboundEmail < ActiveRecord::Base
  self.table_name = "action_mailbox_inbound_emails"

  include Incineratable, MessageId, Routable

  has_one_attached :raw_email
  enum status: %i[ pending processing delivered failed bounced ]

  def mail
    @mail ||= Mail.from_source(source)
  end

  def source
    @source ||= raw_email.download
  end

  def processed?
    delivered? || failed? || bounced?
  end
end

「昼にもちょっと話しましたが、Action Mailerって結構完成度高いというか枯れていて、ある意味ライブラリはこうあるべきみたいな感じなんですが、地味すぎてあんまり光が当たってない気がしますよね」「メール送信の処理自体がだいたい確立していますし☺」「大変なのは運用の方😅

参考: Action Mailer の基礎 | Rails ガイド

「今回、on-by-default incinerationという名目で、Incineratableというモジュール↓が追加されてますね」「いんしねれーしょん?」「いかにも堅苦しそうな語だなと思ったら、焼却って意味だそうです: 処理済みのメールを焼却炉に入れるみたいな感じかなー?」「incinerationをジョブ化して後でまとめて処理もできるっぽい」

module ActionMailbox::InboundEmail::Incineratable
  extend ActiveSupport::Concern

  included do
    after_update_commit :incinerate_later, if: -> { status_previously_changed? && processed? }
  end

  def incinerate_later
    ActionMailbox::IncinerationJob.schedule self
  end

  def incinerate
    Incineration.new(self).run
  end
end

⚓Rails 6のRC版はRailsConf 2019で発表の見込み

ついでに、Rails 6のRC版を来年4月にミネアポリスで開催されるRailsConf 2019で発表する目標になったそうです。

つっつきの後で、改めて発表されました。

「果たしてRCをいくつ重ねるかな😆」「3とか😆

⚓ActiveRecord::Base.connected_to?が追加

?付きです。

# 同PRより
ActiveRecord::Base.connected_to(role: :writing) do
  ActiveRecord::Base.connected_to?(role: :writing) #=> true
  ActiveRecord::Base.connected_to?(role: :reading) #=> false
end

つっつきボイス:「これもマルチDB対応ですね😋」「connected_to?はもうそのまんま」「connected_toは既に実装されたからか」「このrole: :writingって書き込み専用DBのことかな?よくわかんね」「接続の名前かしら?」「スレーブがあるならここではスレーブにつなぎたい、みたいなときに使う感じ」

⚓小数の同値性バリデーションを修正

# activemodel/lib/active_model/validations/numericality.rb#L84
-     def parse_raw_value_as_a_number(raw_value)
-       return raw_value.to_i if is_integer?(raw_value)
-       Kernel.Float(raw_value) unless is_hexadecimal_literal?(raw_value)
+     def parse_as_number(raw_value)
+       if raw_value.is_a?(Float)
+         raw_value.to_d
+       elsif raw_value.is_a?(Numeric)
+         raw_value
+       elsif is_integer?(raw_value)
+         raw_value.to_i
+       elsif is_decimal?(raw_value) && !is_hexadecimal_literal?(raw_value)
+         BigDecimal(raw_value)
+       end
      end

つっつきボイス:「小数の同値性、特に小数以下1桁の数値の場合に不備があったみたいです」「ありそうな感じ☺」「テストがわかりやすいかな↓」「FloatとBigDecimalの間のequalityの話」

# activemodel/test/cases/validations/numericality_validation_test.rb#L292
+ def test_validates_numericality_equality_for_float_and_big_decimal
+   Topic.validates_numericality_of :approved, equal_to: BigDecimal("65.6")
+   invalid!([Float("65.5"), BigDecimal("65.7")], "must be equal to 65.6")
+   valid!([Float("65.6"), BigDecimal("65.6")])
+ end

「今更だけど、浮動小数点って今どき欲しい人っているんだろか?」「毎年のように0.1+0.2 #=>0.30000000000000004ってなるんですけどみたいな質問がネットで上がってますしね」「初心者向け意地悪クイズになりそう」「小数・分数はRationalで統一できたらいいのにー」「自分もそう思います😢

参考: class Rational (Ruby 2.5.0)

「少なくとも業務で浮動小数点がないと困ることってあんまり思いつかないし」「消費税計算なんかも?」「ああ、そういうところでFloatなんか使いません😎: 100倍して固定小数にするし」「やはりー」「あ、四捨五入はありますけど、どの桁で四捨五入するかというのは要件次第なので」「つまりビジネスロジックの領域?」「そそ、思いっきりビジネス要件」

「Javaも今は知らないけど自分がやってた頃はRational的なものはなかったし、JavaScriptも自作するとかしないとないし…」「そもそもRationalをサポートしている言語って自分はRubyぐらいしか知らないなー🤔」「そういえばPythonにもなかった気がする(ライブラリにありました)」「C#には通貨型っていう分数か小数みたいな型があった気がする↓」「つかあるべき😤」「Mathematicaみたいな数式処理ソフトウェアなら当然持ってるでしょうね」

参考: decimal キーワード - C# リファレンス | Microsoft Docs

「こうしてみると、RubyってPrimeもあったりするし、割と数学方面に明るい気がする」「Matzは数学得意じゃなかったと控えめですけどね☺」「実装したのは他の人かもしれないけど、自分がRubyに惹かれた理由のひとつは、そういう数学系のサポートが割ときちんとしていると思ったからですし」「なるほど!😃さすが数学科出身♾

「昔語りで恐縮ですが、創刊間もないアスキーの書評で取り上げられてた『コンピュータ犯罪』↓で、銀行システムで利子計算の端数を全部自分の口座にこっそり転送していたのがバレて逮捕されたプログラマーの話をちょっと思い出しました」「あー、サラミ法っぽいヤツ😎」「今では古典中の古典の手口ですが😅

参考: サラミ法 - Wikipedia

⚓Active Storageドキュメントのコードを更新

これはコミットから見繕いました。

prepend RemoveUploadedFilesするように変更されました。

# guides/source/active_storage_overview.md#L745
+module RemoveUploadedFiles
+  def after_teardown
+    super
+    remove_uploaded_files
+  end
+  private
+  def remove_uploaded_files
+    FileUtils.rm_rf(Rails.root.join('tmp', 'storage'))
+  end
+end
+
module ActionDispatch
  class IntegrationTest
-   def remove_uploaded_files
-     FileUtils.rm_rf(Rails.root.join('tmp', 'storage'))
-   end
-   def after_teardown
-     super
-     remove_uploaded_files
-   end
+   prepend RemoveUploadedFiles
  end
end

つっつきボイス:「これはドキュメントのリファクタリング」

⚓来年3月のsass-rails終了に伴う対応が進行中

これは6.0.0マイルストーンから見繕いました。

既に今年4月のウォッチでRubyのSassが非推奨になった件を扱いましたが、その後5月に上のPRがオープンしていました。いずれもまだopenです。


つっつきボイス:「従来のRubyで書かれたsassライブラリは使うなと」「dart-sassになるかなと思ったら、C言語ベースのsassc-rubyになりそうな流れ」「確かにsassがライブラリごとに挙動が違ってたりするといろいろツライ😣

⚓Rails

⚓koicさんの記事(Ruby Weeklyより)


つっつきボイス:「なぜかRuby Weeklyでkoicさんの日本語の記事が『日本語読めないけど』と紹介されていたので」「まさにRails 4.2とRuby 2.6の組み合わせはもうないからCIから外したのね」

⚓Gemfileの書き方次第でメモリを節約できる(Ruby Weeklyより)

# 同記事より
gem 'rails' # Used on both web and worker servers.
gem 'sidekiq' # Same here.

gem 'pundit', group: :web # Web-only gem.
gem 'image_magick', group: :worker # Worker-only gem.

つっつきボイス:「要するにワーカーでしか使ってないようなgemはGemfileから外せばその分メモリは節約できると: そりゃそうだ😆」「言われてみれば」「非同期ジョブでしか画像処理しないならImageMagickは書かなくてもいい、とか: そこまでやるかなと思うけど😆」「へーgroup:って書けるんだ」「かなり前からありますね☺」「こういうとこ普段いじらないので😅

⚓Railsのポリモーフィックルーティング(Ruby Weeklyより)

# 同記事より
redirect_to :controller => "posts", :action => "show", :id => @post.id

つっつきボイス:「おおっとこの辺は鬼門のヤツ🧛🏻‍♂️」「記事小見出しのform_formは明らかにform_forのタイポだけどちょっとドキッとしました」「まあまあそのぐらいは☺

⚓url_forは慣れが要る

<!-- 同記事より -->
<%= form_for [@post, @comment] do |f| %>

「そうそう、Railsをやってると、いずれこういうふう↑に書くようになってきますよね」「うんうん」「昔からある書き方」「でも割と思ったとおりにならなかったりする😅」「でも慣れてくるとこう書けるようになってくる」「そうっそうっ😁」「逆に言うと、慣れないとなかなか思ったとおりの挙動にならない: 自分はもう覚えたけどっ😆

「これってform_for特有の書き方でしょうか?」「他でも使いますね: これは結局url_for↓の書式なので、そこで使える[@post, @comment]のような配列渡しの書き方をうまく使えという話」「おー」

参考: url_for — ActionView::RoutingUrlFor

「Railsのurl_forって、一種神がかっているというか⛩どんな形式の引数でも受け取ってよしなに処理してくれるメソッドですね」「ある意味ダメメソッド🤣」「ActionPack系ヘルパーメソッドの中では挙動がかなりキモい👻」「*とか**使う引数コワい…😅

「たとえばモデルを複数使っていてidも複数あるとすると、開発中にidがどれも1にしてみた場合に知らないうちにバグを埋め込んでいたとか、ね😭」「一度わかってしまえば、そんなにかっとんだ挙動にはなりませんけどね」「まあ確かに」

# 同記事より
post_comments_path(@post)

「最初上↑のような*_path的な書き方でずっとやってると、下↓のように書けることに長らく気づかなかったりしますね」「そうっそうっ😤: 上のだとform_forのurl部分が超絶長くなっちゃったりして、後になって何だ下みたいに書けるじゃないかって気づいたり」

<!-- 同記事より -->
<%= form_for [:admin, @post, @comment] do |f| %>

「あと、この[:admin, @post, @comment]という書式を使うと、@post@commentpersistedかそうでないかによって自動的に挙動を変えて仕分けしてくれるようになるというメリットもある😋」「そうなんですよ~」「その意味でルーティングヘルパーよりも賢い🧐」「複数形のsが付くか付かないかも面倒見てくれるですよ❤」「めちゃ高機能」「つかやりすぎだろうって🤣

「実際[:admin, @post, @comment]という書式の方がたいていシンプルに書ける」「DRYに書ける😎」「ドライ😎」「自分はこの書式で書くこと多い」

「ただ、モデルに名前空間が付いているとそれはそれで面倒くさい😅」「一個名前空間付ければいいとかじゃなくて?」「たとえば@commentがHogeHogeモデルのcommentだったりするとHogeHogeも入ってきて、む〜ん残念という感じになったり😆」「typeも指定したりとか😆」「という感じでこれはこれでカオスになったり🤪

⚓RailsのテンプレートレンダリングとAction Viewを理解する

# 同記事より
require "erb"
# let's pretend it's ActionView::Base
class View
end

class Template
  def initialize(name, content)
    @name = name
    @content = content
  end

  def render(view)
    compile(view)
    view.send(@name)
  end

  def compile(view)
    # we only need to compile it once
    return if @compiled
    # use erb as template engine
    body = ERB.new(@content).src
    src = <<-end_src
      def #{@name}
        #{body}
      end
    end_src

    view.singleton_class.module_eval(src)

      @compiled = true
  end
end

view = View.new
template = Template.new("say_hi", "Hi!")
template.render(view) #=> Hi!
view.methods.first #=> :say_hi

つっつきボイス:「これは例のGobyのst0012さんが書いた、最近のRailsテンプレートレンダリングを解説する記事です: 近々翻訳しようと思います」「そういえばRailsのテンプレートレンダリングってある時期に変わったんだけど、その後は何か変わってるのかな?🤔

「テンプレートレンダリングを素で使ってみる記事っぽい」「このあたりは、前に例のCrafting Rails 4 Applicationsの読み会↓をやったときに扱いましたね: 当時はRails 4ベースなので今はどうなってるかわかんないけど」「当時はRailsそのものの枠組みがいろいろわかってなくて振り落とされないようにするのが精一杯で、記憶がほとんどない…😅

【勉強会報告】Crafting Rails 4 Applications読み会をはじめました+第一回資料

「st0012さんは記事の最後でAction Viewの設計はよくできてると述べてますね」「Rails 3までのテンプレートレンダリングは結構泥臭い感じだったんですが、Rails 4で汎用テンプレートライブラリ的に書き直されてかなり垢抜けてきて、フォーマットレンダラーを自分で代入したりもできるようになってた」「ほー!」「Crafting Rails 4 Applicationsは結構いい本だったので、Rails 5版もあるといいんだけど」「5は出てないみたいです…😢

「お、ちょうど第1回読み会でこの辺扱ってた(スライドp18)↓: 自分でRendererを作って理解する章」「ほー、こういう感じですか!」

「こぼれ話ですけど、st0012さんが『次はどんな記事がいい?』と聞いてきたので、『Railsアプリの設計記事とかいいかも』と伝えたら『今仕事でやってるアプリでa “special” module patternが使われていてなかなか面白いので、許可出たら要点を記事にしてもいいよ』と言ってました」「モジュールパターンにもいろいろありますけどね😆」「a specialということしか私も聞いてなくて😅

⚓ちょっと便利なRackミドルウェア(Ruby Weeklyより)


つっつきボイス:「自分でRackミドルウェアを作る感じ」「まずはbinding.pryするだけのミドルウェアを作って、ミドルウェアスタックで一旦停止できるようにすると↓」「Potatoってミドルウェアがあるのかと思ったら手作りでしたか💦

# 同記事より
class Potato  
  def initialize(app)
    @app = app
  end

  def call(*args, &blk)
    @app.call(*args, &blk)
  rescue BasicObject => e
    require 'pry'; binding.pry
  end
end

use Potato  
run Rails.application  

Stackprofはファイラー的なヤツ: そうそう、この辺もさっきのCrafting Rails 4 Applicationsでやってるんですよ〜😎」「お恥ずかしや😅

「こうやってRackミドルウェアの動作を知っておく価値は非常に高いっすね」「自分で作ったミドルウェアをproductionで使うのは結構勇気要りますけど😆よっぽど単純なものならともかく」「でしょうね〜」

「最後にあるunicorn-worker-killerって、以前のつっつきbabaさんが昔よく使ったって言ってた、ワーカープロセスを殺しまくるヤツでしたっけ?」「今も使われてるかも: Unicornのワーカーが際限なくメモリを食いまくってたんで、強制的にメモリを解放するためにkillするヤツ」「やはり」

⚓i18nをやってみた(Hacklinesより)


つっつきボイス:「まだ駆け出しだそうですが、非日本人が日本語アプリのi18n頑張ってるので珍しいなと思って」「やってることはまったく普通のi18nですね」「JA、オレにも読める…!😋」「プロファイル見ると、この人松本市に住んでるのかー」「女性」「skill languageにちゃんとJapaneseって書いてある」

⚓最近のRuby向けPDF生成ライブラリ(Hacklinesより)


つっつきボイス:「おー2018年版!」「wkhtmltopdf、まだあるんだ!」「すっごく昔からあるヤツって以前のつっつきにも出てましたね」「wkhtmltopdf最近使いましたよね😎」「覚えてない…😅」「ほら、透かしを入れるときに」「あー使ってたわ!」


同サイトより

prawnは知ってるけどHexaPDFって知らなかった!」「ずうっと前のウォッチにちょっと出てきてた」

「HexaPDFはPDFのマニピュレーションやマージができるのか: 機会があったら試してみようかな😋」「relative newcomerとあるから新し目だし」

「PDFライブラリって、何をやるかによって全然ライブラリが違うんですよね: 既存のPDFファイルを開いて何か操作したいときって意外と選択肢が少ない😓」「そういう事情で当時はwkhtmltopdfしか使えなかった覚えがある」「日本語PDFもやっぱり大変でしょうか?」「うーん、フォント指定ができるライブラリならだいたい大丈夫だとは思うけど」「そういえばフォントはfontconfigから引っ張ってくるタイプと、cairoとかから引っ張ってくるタイプがあったけど、だいたいfontconfigでできるんじゃないかな」「カイロってこれかー↓」「二次元画像ライブラリですね」「この虫、フンコロガシ?」

参考: Fontconfig - TeX Wiki
参考: cairo - Wikipedia


cairographics.orgより

なぜかナレーターが途中で吹き出してます↓。

⚓AWS LambdaでRubyとSinatraやってみた(Ruby Weeklyより)


同記事より


つっつきボイス:「今旬のやってみた系記事です」「あーLambdaでSinatraかー、Sinatraだと軽いのかなーどうなんだろうな〜?🤔」 「ミドルウェアが重くなりそうだから、ミドルウェアを相当シェイプアップしないと厳しそうな気も」「Rackミドルウェア風にする必要がそもそもあるかどうか」「プロセスは毎回終了するだろうし」

「この辺については、はてブにも上がっていたこの記事↓がかなりよくできてて一読の価値がありますね」「おー!」「Lambda Custom Runtime周りをbashで詳細に追っていてありがたい🙏」「この記事が何を言っているのかが理解できればだいたい大丈夫☺: 前提としてマイクロサービスやLambdaの知識が必要」

参考: Lambda Custom Runtimes上でbashを対話的に操作してその内部仕様を丸裸にする - misc.tech.notes


同記事より

⚓その他Rails


つっつきボイス:「たまたま見つけたAction Cableクライアントです」「RubyのWebsocketクライアントってことですよね?RubyでWebsocketに接続しにいくことってなさそう〜」「テストのときぐらいでしょうか?」「テスト用ならワンチャンあるかもしれないけど、それこそJavaScriptでWebsocketを叩く方が確実☺」「そうでしたか💦」「WebsocketをどうしてもRubyから叩きたい人が使うのかも」

「Action Cableはそんなに詳しく知らないんですが、はっきりとした専用プロトコルがあるわけでもなくて、ほぼ素のWebsocketに近い印象ある: このgemも、Action Cable Clientと名乗るより、ストレートにWebsocket Clientという名前にする方がアピールするかも?」

⚓Ruby

⚓🌟Jets: Ruby製サーバーレスフレームワーク🌟


同リポジトリより


つっつきボイス:「Jets、聞き覚えがあるようなないような」「これはまさに、さっき上で話したみたいにミドルウェアが重くなったりしないよう軽量に構築する感じ」「Lambdaでも動く軽快なミドルウェアで、かつRailsライクなコードで書ける、という狙いっぽい」「お、お〜😳


同サイトより

# 同サイトより: コマンド群
jets new
jets generate scaffold
jets db:migrate
jets server
jets console
jets deploy
jets routes
jets call
jets status
jets url
jets delete
# 同サイトより: コントローラ
class PostsController < ApplicationController
  def index
    # renders Lambda Proxy structure compatiable with API Gateway
    render json: {hello: "world", action: "index"}
  end

  def show
    id = params[:id] # params available
    # puts goes to the lambda logs
    puts event # raw lambda event available
    render json: {action: "show", id: id}
  end
end

「ローカルでもサーバーを起動できるし」「Active Record読み込んでるあたりは結構重そうな気もするけど😆、AR動くなら悪くなさそう」「ふむふむ」「ぱっと見Rails互換ぽく書けるというのはそれだけでちょっとありがたい感」

「今どき素でメッセージ展開とかしたくないし😆」「したくない〜」「単体のメッセージを処理するぐらいならともかく、ある程度Webアプリらしく複数のアクションをさばくようになってくると、こうやってRails wayっぽく進められる方が困りにくいというか設計がぶれずに済むし」「それはある」「こういうのならRailsエンジニアが移行するにもラクだろうし、宗教論争にもなりにくいだろうし⛩

「これ↓みたいに完全にfunctionで書くこともできれば、上みたいにコントローラを書けばRailsのコントローラ的なこともできるようだ」

# 同リポジトリより: app/functions/simple.rb
def handler_function(event:, context:)
  puts "hello world"
  {hello: "world"}
end

「AWSのAPI Gatewayでもこんなふうに↓できるみたいだし」「あらステキ❤」「デプロイもできる?」「デプロイコマンドありますね!jets deploy


同リポジトリより

「ちゃんと動くんならだけど😆、悪くなさそうに見える☺」「冬休みに遊ぶのに手頃かも」「すごく新しいようです」「タイミング的にもLambda Custom Runtimeがリリースされてから出したんでしょうね」

期待を込めて久しぶりに🌟を進呈いたします。おめでとうございます。

⚓WebMock: Rubyでスタブやexpectation(Ruby Weeklyより)

# 同リポジトリより
stub_request(:post, "www.example.com").
  with(body: /world$/, headers: {"Content-Type" => /image\/.+/}).
  to_return(body: "abc")

uri = URI.parse('http://www.example.com/')
req = Net::HTTP::Post.new(uri.path)
req['Content-Type'] = 'image/png'

res = Net::HTTP.start(uri.host, uri.port) do |http|
  http.request(req, 'hello world')
end    # ===> Success

つっつきボイス:「これってあるとうれしいヤツでしょうか?」「HTTPのモックか〜: これってなかなか大変なんだよな〜」「というと?」「HTTPをどのレベルでモックするかは要件によって相当変わってくるんですよ: たとえばTCPソケットのような低いレベルでモックしたいときはあまりリッチなライブラリだと困るし、逆にHTTP requestレベルでモックしたいならリッチなものが欲しいし、とか」「確かに〜」「お、このwebmockは割と何でも扱うっぽい」「webmockって結構前からあった気がする」「一番古いリリースが2010年2月ですね」

「まあ普通にrequest specとか書く分には、今ならここまでHTTPモックすることはないですね」「でしょうね」「使うときがあるとすれば、文字通りHTTPレベルで細かくチェックしたいとき: こういうリクエストを送ったらこういうchunkedが返ってこないといけない、とか、HTTPヘッダーに特殊なものを差し込んでいてそれをチェックする、とか」「なるほど!」「Railsフレームワークのテストとかで使ってそう」「後はSSOサーバーをチェックするときとかかな」「それありそう〜」「SSOって、えっと」「シングルサインオン」

参考: シングルサインオン - Wikipedia

⚓Rubyの+=<<Hacklinesより)

<<のShovelってショベル?」「やっぱりそう思いますよね?自分も初めてその呼び方見ました」「ショベル演算子…」「他の呼び方はなかった気がする」

「Rubyの+=<<って、慣れるまでは割と違和感あるというのはちょっとワカル」「+=<<は、Stringに対して使うと挙動が同じなのに、Arrayに対して使うと挙動が違う」「あ〜そうそう!」

「あとは記事にもあるけど、=は代入だから相手は変数だけど、<<の場合は相手がオブジェクト」

「ここは推測だけど、+=みたいなのってa = a + 1のショートハンドだから、+=の形のときだけオーバーライドするとかはできなさそう: <<はできそうだけど」「記事ではそこまでは話してないけど」

参考: Ruby | 再定義できる演算子 で組み込み演算子風のメソッドを定義する #ruby - Qiita

⚓その他Ruby


つっつきボイス:「grpc_kitみたいなクライアントがあるとありがたいときがありそう」


(以下はRuby Weeklyより)

⚓Ruby trunkより

⚓非ASCII文字でのキャプチャ

# 同issueより
# rewrite_constant_by_named_capture.rb
class C
end

/(?<C>C)/ =~ "C"

p C

class 𝐂
end

/(?<𝐂>𝐂)/ =~ "𝐂"

p 𝐂

C.new
𝐂.new

つっつきボイス:「知ってる人が投げてるissueだったので」「非ASCII文字で攻めてる」「『これは意図的ですか?』って、切り込んでくるな〜😆」「これよく見つけた感」

⚓クラウド/コンテナ/インフラ/Linux/Serverless

⚓Kinsta: GCPベースでWordPressをホスティング


同サイトより


つっつきボイス:「WordPress向けのホスティングサービスですが、WordPress/PHP方面の知人が以下の点に強く関心を抱いていたのでピックアップしてみました」

  • サイトごとにstaging環境をセットでき、確認後ワンクリックでLiveに持っていける
  • GCPをフル活用
  • LXDコンテナを使用
  • sshログインしてフルカスタマイズも可能

「staging環境からワンクリックで本番に持っていけるというのはちょっとよさそう」「WordPressでサイトを構築し始めると、あっというまにサイトが数十個とかになりがちだし😆、その意味でこういうサービスに任せられるならいいかもしれない」「WordPressでやってるようなサイトは予算少なそうな印象🤣」「あとはこのサービスが今後なくならずに盛り上がってくれるかどうか、かな😆」「GCPそのものはあまり心配なさそうだけど」

⚓LXDとは


同サイトより


LXD は次世代のシステムコンテナマネージャです。 仮想マシンと同じようなユーザ体験を提供しますが、仮想マシンではなく Linux コンテナを使います。
LXD は、多数の Linux ディストリビューションが利用できる、あらかじめビルドされたイメージをベースとしています。そして、とてもパワフルでありながら、とてもシンプルな REST API を中心に構成されています。
LXD がどのようなもので、何ができるのかをよく理解するために、オンラインで使ってみることができます! さらに、LXD をローカルで実行したい場合は、「はじめに」 のページをご覧ください。
LXD プロジェクトは Canonical Ltd によって設立され、現在は様々な企業や個人のコントリビュータの貢献のもとで Canonical Ltd が主導しています。
同サイトより


つっつきボイス:「上のKinstaの話を聞いたときに最初LXCかと思ってさらっと見逃してたんですが、よく見たらLXDだったので」「次世代システムコンテナマネージャか: Dockerやsystemd-nspawnと同じようなレイヤかな?🤔」「UbuntuでおなじみのCanonical Groupがやってるんですね」「Go言語で書かれてる」

参考: LXC - Wikipedia

超シンプルなコンテナ管理systemd-nspawnを使ってみる

以下のスライドは2017年のですが、DockerとLXDの違いが手短に説明されていました。

  • スライドの要点
    • LXDはLXCの上のレイヤにあり、ubuntuホストで別のLinuxディストリビューションを実行する
    • LXDはVM alternativeであり、複数プロセスを扱うコンテナであり、OSであり、IaaSである
    • Dockerはアプリをカプセル化するシングルプロセスのコンテナであり、PaaSである

LXDには今のところDocker Hub的なリポジトリはないようです。

⚓Windows Sandbox(Publickeyより)


つっつきボイス:「babaさんが喜びの声を上げていたので」「Windows 10 Pro またはEnterprise専用で、Hyper-Vベース: Windowsのライセンスを追加で買わなくてもこの記事↓でやったようなWindows on Windowsができるということに😋」「それ重要😍!」

Web開発環境をMacBook ProからWindows機に移行してみた話

「後はどのぐらいちゃんと動いてくれるかというのと、Publickey記事の『そしてWindows Sandboxが終了すれば、すべてのソフトウェアはそのファイルや状態もろとも完全に削除される』という一文が気になる😅」「😅」「きっとイメージとかは作れるんだと思うけど、途中のイメージが作れなかったらどうしよう😅

⚓SQL

⚓PlanetScale: YouTubeの元CTOが立ち上げたスケーラブルMySQLクラスタ(DB Weeklyより)


同サイトより


vitess.ioより


つっつきボイス:「スケールできるMySQLクラスタサービスか: Percona版MySQLみたいな感じなのかな?🤔: ベースのVitessはオープンソースだから自分たちのサーバーでも動かせそう」「Vitess、★7000超え!」

「YouTubeのバックエンドではこの人のやってたVitessを使ってるという触れ込みでした」「ほほー!」「この辺は永遠のテーマ😎」「そういえばいつだったかTwitterがCassandraで失敗したみたいな話もあったし」「やっぱり名前が不吉だったんでは😆

参考: Twitterが、Cassandraの本採用を断念。「いまは切り替えの時期ではない」 - Publickey

Twitterのアーカイブリポジトリにこんなのがひっそり置いてありました↓。

⚓JavaScript

⚓Pika: npmの新し目のパッケージを検索するサービス(JavaScript Weeklyより)


同サイトより


つっつきボイス:「トリビアかなと思いつつ」「人気順で検索できると: ESモジュールかどうかがわかるのはちょっといいかも😋

⚓CSS/HTML/フロントエンド/テスト

⚓パフォーマンス駆動開発とは(Frontend Weeklyより)


同記事より


つっつきボイス:「また何とかドリブン開発が出てたので」「ざざっと眺めた限りでは、Performance AuditをCIで回しておくことで、パフォーマンスの問題を早期に把握して対応しようという趣旨でしょうね」「あーなるほど!」「実際それ大事だし🧐」「分野にもよるけど、めちゃくちゃ高いパフォーマンスを要求されるアプリなんかは、そうやって開発を進めないと、残念なコミットをデプロイした瞬間にサービスが落ちかねないので😇」「やってることはCIチェックなんだけど、こういうちょっとかっこよさげな名前を付けることで諸事動き出したりしますよね☺

⚓言語

⚓Rust 2018 Editionリリース(Publickeyより)

breaking changes入りだそうです。


つっつきボイス:「勇気あるbreaking changes」「Rust使ってる人たちならOKっしょ😆」「社内Rust勢も色めき立ってました」

「こうやってある程度普及した言語でbreaking changesが入ることで、その後の使い勝手がうんとよくなったりしますよね: Rubyも1.8から1.9でbreaking changesを決行しましたけど、あれをやってなかったら今Ruby使うのは相当つらかったと思うし😅」「わかりみ〜」「やはりbreaking changesはどこかで必要になってきますね☺」「やるなら早いうち!」

参考: Ruby 1.8/1.9/2.0 系の違い - 君の瞳はまるでルビー - Ruby 関連まとめサイト
参考: Ruby 1.9に移行する際に注意すべき10のポイント - なんとなく日記

⚓Go言語が段階的に$GOPATHから「Goモジュール」に移行

結構な大工事になりそうです。

  • Go 1.11から1.13にかけてGoモジュールへの対応を段階的にすすめる予定
  • 乱立しているlintツールを統合し、language serverプロトコルに対応させる予定
  • モジュールの統合アクセスや認証を進め、中間者攻撃対策も行う
  • 最終的にパッケージをGoモジュールに移行し、GOPATHの外でも使えるようにするのが目標

つっつきボイス:「Goモジュールに段階的に移行するそうです」「$GOPATHもいろいろ無理が出てきたのかな😆」「今だと$GOPATHの外に出ると扱えないし😢

「推測ですが、今の$GOPATHを使う設計はもっとささやかなマイクロサービスとかを作ることを念頭に置いてたんじゃないかって」「私もそんな気がします: Makefileを書きたくない、とか(自分もキライ😆)」「Makefileを書くような規模になったら、他の言語でやっとくれ、ぐらいの勢いだったりして😆

参考: 2016年だけどMakefileを使ってみる - Qiita

「そして色んな人が大規模なGoコードをどかすか書いてるし😆」「Dockerとか😆」「やっぱり言語が人気を得てくると、言語設計者の当初の想定を超えた使われ方をするということで☺

⚓その他

⚓はんだ付け


つっつきボイス:「こちらのアドベント記事↓ではんだ付けの話題になったので」「はんだ付けを練習できる教材のページ」「上の記事にある『NPO法人 日本はんだ付け協会』って😆」「初耳…!😆

Iris: はじめての自作キーボード 〜 失敗談とその解決方法 〜


handa-npo.comより

「はんだ付けっていっぱいやってました?」「そんなにやってないかなー: 一番やってたのはアマチュア無線部の頃で、それもいわゆる精密なはんだ付けなんかじゃなくて、屋外アンテナによじ登ってケーブルを60Wぐらいのでかいコテでえいやっと付けたり😆」「あーそっちの方ですか😆」「熱量要るヤツ」「ほとんど溶接」「後はたまに秋月電子のキットを組み立てたときぐらいで、ICのはんだ付けはしたことないかな」「小学校の頃に某ワンボードマイコンを喜び勇んではんだ付けしまくって組み立てたら、見事ICを1個逆に挿してたことありました😅」「あるある〜😆

「はんだ付けは、慣れもあるけど道具が大事っすね」「先っちょはいいものを買おう!」

「はんだ付けの他にワイヤラッピングっていう結線法も自分の頃からあったんですが、何となく高そうで手が出ないうちに下火になりつつある感😢」「あー、こういう線を巻き巻きしてつなぐヤツ」「こう見えて、いったん巻くととても安定するらしいです」「携帯端末が毎月のように新しく出ていた頃なんかはこういうのでプロトタイプをどんどん組み立ててたかもですね☺」「今もありそうだけど、今だと集積度上がりすぎてこうやって巻く余地がなさそう」

参考: ワイヤラッピング - Wikipedia

⚓番外

⚓土星の環は将来消えるのか


つっつきボイス:「1億年😆」「このスケールがもはやピンとこない😆」「人類いるのか😆

参考: CNN.co.jp : 土星の環、1億年足らずで消滅か NASA研究


今回は以上です。

おたより発掘

バックナンバー(2018年度後半)

週刊Railsウォッチ(20181210)update_columnは要注意、DBカラムコメントは書こう、個人情報扱いの注意点、Capistranoはやっぱりいいほか

今週の主なニュースソース

ソースの表記されていない項目は独自ルート(TwitterやRSSなど)です。

Rails公式ニュース

Ruby Weekly

Hacklines

Hacklines

Publickey

publickey_banner_captured

DB Weekly

db_weekly_banner

Frontend Weekly

frontendweekly_banner_captured

JavaScript Weekly

javascriptweekly_logo_captured

Rails開発者向けのWordPress開発入門#1: 概要(社内勉強会)

$
0
0

書籍『エンジニアのためのWordPress開発入門』について

『エンジニアのためのWordPress開発入門』は、WordPress独自の構造やコーディング作法について、開発者の立場から詳しく解説している良書です。

著者の多くがRailsエンジニアなので、Railsとの対比でWordPressを理解できるのも大変ありがたい点です。

本記事では、徳丸浩先生のセキュリティ講座で得た知見も少し交えて同書の最小限の概要を紹介します。WordPressのインストール方法や技術的な詳細については触れません。

本記事で概略を押さえておくことで、同書をよりスムーズに読めると思います。

  • 対象
    • 急にWordPressで開発をしなければならなくなったRailsエンジニア
    • WordPressを使ってる人、使い始めた人

WordPressとは

一言で言えば、PHPで書かれたオープンソースのCMS(Content Management Service)です。

WordPressの日本公式キャラクター「Wapuu」

類似のCMSソフトウェアは山ほどありますが、最大の特徴はとにかくシェアが世界最大であり、ユーザー数も大変多いという点です。

長年にわたって広く使われているため、多くのノウハウが蓄積されており、特にマーケティングやSEO方面では基本ソフトウェアのひとつに数えられるほどです。

テーマやプラグインの数も膨大なので、やりたいことの多くはプラグインを少し探せばたいてい見つかります。

WordPressは、コンテンツやタグ情報の保存のために背後にMySQLなどのRDBMSを配置する必要があります。RDBMSのテーブルは、よほど特殊な使い方をしない限り(ブログを超えるような使い方など)増やす必要はないでしょう。

WordPressでもうひとつ見逃せない点は、管理コンソールの完成度が高いという点です。外部向けのテーマはさまざまですが、管理コンソールはどのWordPressでも基本的にさほど違わないので、開発者でないエンドユーザーにも使ってもらいやすいものになっています。

案件によっては、Railsで管理画面を構築する代わりにWordPressを管理画面専用として使う方法も考えられます。

WordPressはCMSではありますが、フレームワークと呼べるほど一般性は高くありません。同書および本記事ではRailsとの比較を行っていますが、多少無理矢理の感があります。

WordPress開発の重要な資料

WordPress Codex 日本語版

WordPressの一次情報といえばこのCodexです。ありがたいことに情報の多くは日本語化されており、API情報を含む開発情報が豊富に掲載されています。

WordPress 日本語公式サイト

セキュリティ情報やアップデート情報はここから得られます。

サポートフォーラム| WordPress.org

WordPressのプラグインごとに独自のフォーラムがあります。フォーラムの多くは英語です。

なお、以前はWordBenchというユーザーコミュニティがありましたが現在は終了し、Tokyo WordPress Meetupなどが後を継いでいます。

参考: WordBench 終了に寄せてのポエム的なコメント – Gatespace’s Blog

徳丸浩の日記

徳丸浩先生といえば、セキュリティ、特にWordPressやPHPのセキュリティでは筆頭に挙がります。こちらのブログをフォローすることをおすすめします。

Railsエンジニアから見たWordPress

同書によれば、Railsエンジニアにとって「WordPressは決して美しいコードではない」とのことです。

  • MVCという発想ではなく、処理の流れにひたすらフックでアクションやフィルタを追加する
  • グローバル変数が大量に使われている
  • 変数や関数のネーミングがかなり適当
  • マルチスレッドという考え方がほぼない

RailsとWordPressの比較

同書を頼りに、RailsとWordPressの処理の流れを図にしてみました。RDBMSは図に含めていません。

一見してわかるように、RailsはMVCに沿った構造になっていますが、WordPressは「ルーティング」「リクエスト解析」「メインクエリ」「テンプレート適用」という基本的な処理にフックをかける形で機能を追加していきます。

wp_cronという、一見cronらしき関数がありますが、これすら実際はcronプロセスが常駐するわけでもない、単なるイベントドリブンの関数です。

以上のように、WordPressはRailsのようなWebフレームワークとはかなり様子が異なっています。にもかかわらず同書では「それを理解したうえでなら十分付き合える」という結論に達しています。

WordPress vs Webフレームワーク

WordPressは、用途を十分注意深く選ぶ限り、たとえばRailsやCakePHPのようなWebフレームワークやEC-CUBEなどでスクラッチから構築するよりも適していること「も」あります。

用途の決め手となるのは「ブログシステムの範疇を超えないかどうか」です。以下のような要素を満たしていれば、WordPressからはみ出さずに短期間で有用なサイトを構築できる可能性があります。

  • コーディングやデータベーステーブルの追加を避けられる
  • カテゴリやタグで分類する運用
  • 固定ページを活用できる
  • プラグインで対応できる
  • テーマは既存のものを最小限に改修することでまかなえる

これを超える可能性があるなら、最初からWebフレームワークで構築する方がよいでしょう。クライアントの要件が将来これを超えることがないかどうか、事前によく確認しておきましょう。

WordPressをどこに置くか

案件にもよりますが、WordPressを顧客が運用するのであれば、まずWordPress専用のホスティングサービスの利用を検討してもらうのがおすすめです。

専用ホスティングサービスであれば、WordPressやプラグインのアップデート、特にセキュリティアップデートについてまとめて面倒を見てもらえます。これは大きなメリットです。

セキュリティアップデートがなされず放置されたままになっているWordPressサイトは驚くほどたくさんあります。

また、こうしたサービスはWordPressに特化した高速化がなされているのが普通なので、パフォーマンスの懸念点も減らせます。

さまざまなホスティングサービスがありますが、たとえばKUSANAGIは、AWS/Azure/IBM Cloud/GCPなどでWordPress仮想マシンサービスを利用することもできます。

WordPressの「テーマ」

WordPressのテーマには、画面構成、表示のほかに若干の基本機能が含まれます。WordPressのWeb画面は、既存テーマを元にカスタマイズする方法が主流ですが、スクラッチでテーマを構築することもできます。

twenty何とかといったデフォルトのテーマもありますし、サードパーティ製のテーマも多数あります。もっとも、twentyなんちゃらは凝りすぎているのでもっと素直なテーマがよいかもしれないという意見もあります。

参考: WordPressテーマの選び方- 初心者ならデフォルトテーマが無難

ただしサードパーティ製のテーマは、マイナーなものを選ぶとメンテされないままセキュリティホールが見つかるなどの可能性があるため、利用者が多く確実にメンテされそうなテーマを選ぶようにしましょう。

テーマをカスタマイズする場合、元テーマの子テーマを作成するのがよいでしょう。こうすることで親テーマのcssや関数を継承でき、最小限の手間でカスタマイズを進められます。

テーマを認識するためには最小限以下が必要です。

  • style.css: これが意外に重要で、親テーマもここから指定します
  • index.php

WordPressの「テンプレート」

WordPressのテンプレートは、表示を担当するPHPファイル群であり、MVCで言うところのVに近い位置づけです。PHPファイルにはHTMLも記述できます。

1つのPHPテンプレートファイルでまかなうこともできますが、メンテナンスしやすいよう、ヘッダやフッタやサイドバーなどを部分テンプレートファイルに分割して、メインのテンプレートファイルでインクルードするのが普通です。

テンプレートを書くときのポイントとしては、ifなどのロジックをできるだけ書かないことです。そうしたロジックは次にご紹介するfunctions.phpに記述し、テンプレートから呼び出すようにします。

WordPressの「functions.php」

themesフォルダ以下のphpファイルの中でもfunctions.phpは少し特別で、WordPressで使う関数はここに集約します(テンプレート表示用に限りません)。

functions.phpは、Railsで言う「ビューヘルパー」に相当すると理解できます。長くなりすぎるようなら別ファイルに分割してrequireで読み込むこともできます。

テーマのfunctions.phpは子テーマにも継承されるので、子テーマのfunctions.phpでは差分だけを書けばよいことになります。もっともそれが邪魔になることもあったりしますが。

WordPressの「フック」

前述の概要図に示したように、WordPressの内部にはwp_loadedparse_requestなど多数のフックが用意されています。基本的にはこれらのフックを用いて操作します。

フックは大きく分けて以下の2種類がありますが、この使い分けは実際には割りと曖昧なので、Codexの記述を優先し、囚われすぎないようにしましょう。

アクション
主に処理の追加に用いる、値を返さない
フィルタ
主に処理の変更に用いる、値を返す

WordPressの「プラグイン」

WordPressには豊富なプラグインが存在し、これらを用いてWordPressの機能を簡単に拡張できるようになっています。

プラグインはPHPで記述されます。また、アップデート情報を管理コンソールで自動通知する機能もあります。

プラグインが簡単に利用できてしまうため、特にエンドユーザーがほいほいインストールしてしまうことがありますが、プラグインの安易なインストールはできるだけ避けましょう。後で自分たちが苦しみます。

  • ものにもよりますが、プラグインをインストールすればするほどサイトは確実に重くなります
  • プラグインのメンテナンス状況はさまざまなので、閑古鳥の鳴くプラグインを入れっぱなしにするとセキュリティーホールの原因にもなります。

  • さらに、機能の似たプラグインをいくつもインストールすると互いにコンフリクトしてしまうこともあります。

  • プラグインをインストールすればするほど、管理コンソールにアップデートを促す通知がいつも点灯し、管理コストを押し上げます

WordPressのプラグインは常に厳選し、html/css/素のjavascript側で十分対応できることにまで使うのは控えましょう。

WordPressで絶対インストールしておくべきプラグイン

例外として、徳丸先生も推奨するSiteGuard WPプラグインは必ずインストールしましょう。このプラグインはWordPressのセキュリティをチェックします。

WordPressはCMSの中でも歴史が長く、致命的なセキュリティホールはひととおりつぶされている方なので、近年は「それほど」巨大なセキュリティホールは発生していません。

設計のまともなCMSは次から次に作られていますが、長年の風雪に耐えて利用され続けているWordPressの実績を超えるのは並大抵ではありません。

セキュリティはプラグインだけに頼らないこと

WordPressに限らず、Webサイトのセキュリティはプラグインなどのソフトウェアだけで高められるものではありません。運用に不備があれば、防御にどれほどコストをかけても台無しになってしまいます。

以下に徳丸先生の教えをメモします。

  • 弱いパスワードを使っているユーザーが1人もいないように運用すること
  • WordPressとプラグインを常に最新の状態にアップデートすること

WordPressは比較的頻繁にアップデートされますが、それが脆弱性の修正であれば必ずアップデートを適用してください。また、WordPress本体がアップデートされると、それに伴ってプラグインも後追いでアップデートされることが多いので、本体アップデート後はしばらくの間注意を払っておきましょう。

当然ですが、既存のテーマを(子テーマにせずに)複製してカスタマイズすると、テーマのセキュリティホールを自力で修正しなければならなくなることもありえます。独自テーマはその意味で不利と言えます。


おたより発掘

関連記事

「徳丸浩とWordPress セキュリティを学ぶ夜」に参加いたしました

WordPressをローカル環境等にお引っ越しする方法(XAMPP使いも歓迎)

Rails開発者向けのWordPress開発入門#2: 開発上の注意(社内勉強会)

$
0
0

WordPressをもう少し詳しく知る

Railsにないもの/あるもの

以下はRailsとの大きな違いを列挙したものです。「ない」というのは「まったく存在しない」のではなく、「わかりやすい形では集約されていない」と考える方がよいでしょう。

  • RailsにあってWordPressにないもの
    • MVC構造(特にコントローラとモデル)
    • ファイル配置の厳密な規則
    • ルーティングファイル(Railsでいうroutes.rb)
  • RailsになくてWordPressにあるもの
    • 基本的なデータベース構造
    • 標準の管理画面
    • プラグイン
    • テーマ
    • 「カテゴリ」「タグ」による2本立ての分類機能
    • ウィジェット

Railsは汎用的なWebフレームワークなので、MVCを基本にさまざまな構成を取れるようになっています。SPAなどのためにMVCを崩すこともありますが、その場合「Rails Way」から外れることになります。

一方WordPressはRailsよりも目的が絞り込まれており、ブログシステムに特化しています。データベース構造は基本的に変更しない前提なので、データベース構造を変えるとブログシステムから外れることになります。

WordPressのデータベース構造

WordPress 4.4.2のデータベース構造は以下のようになっています。かなりシンプルに抑えられているのがわかります。


codex.wordpress.orgより

以下は主なテーブルです。

  • 基本テーブル
    • wp_posts(投稿データ)
    • wp_users(ユーザー)
    • wp_comment(コメント)
    • 上のメタデータテーブル
  • 分類用テーブル(多対多)
    • wp_term_relationship
    • wp_term_taxonomy
    • wp_terms
  • 設定用テーブル
    • wp_options

注意: サイトのドメイン情報はデータベースに保存される

WordPressでは、サイトのドメイン情報をデータベースに保存するようになっています。

つまり、WordPressの全ファイルとデータベースを別サイトに移しただけでは動かないということです。

たとえばWordPressをバックアップして、それを元に開発環境やstaging環境を作る場合、スクリプトからSQLを発行してデータベース内のドメイン情報を明示的に書き換えなければなりません。

ドメイン名を設定ファイルに保存する作りになっていたらどんなに楽だったかと思います。

WordPressのフックは「コンテキスト依存」

先に挙げたWordPressの構成図に記載したフックは大まかなものであり、実際には「データ取得」「ヘッダー出力」「コンテンツ出力」「フッター出力」などの細かなタイミングでおびただしいフックがあります。

そしてWordPressのフックはコンテキストに強く依存するため、「ある関数を事前に実行しておかないと正しい値が取れない」といったことがしばしばあります。

こういうのを目にするとRailsのありがたみを痛感します。

WordPressでCSSやJavaScriptを追加する場合

WordPressでCSSやJavaScriptを導入する場合、通常のようにテンプレートファイルに生で書くのではなく、wp_enqueue_script()wp_enqueue_style()を用いてfunctions.phpに記述することが推奨されています。忘れがちなのでご注意ください。

参考: functions.phpでJSやCSSを一元管理する - SMART 開発者のためのウェブマガジン

なお、AMP用のテーマではAMPの仕様による制約のためwp_enqueue_script()wp_enqueue_style()が利用できませんので、別のフック関数を用います。

参考: WordPress AMP対応テーマやプラグイン作成に重要なAMP判定と4つのアクションフック – セルティスラボ

WordPressの「メインクエリ」

メインクエリは、WordPressにおける重要な概念です。


wpdocs.osdn.jpより: CC-BY-SA

WordPressでは、リクエスト解析が終わってリクエストパラメータが確定するとき、それによって行われるデータ検索を「メインクエリ」と呼びます(上の図の赤いquery_posts)。

$wp->query_posts()

フックやプラグインは、このメインクエリに対して操作を行います。

注意: メインクエリの直接操作は避けること

query_posts()をテンプレートに書くと、記事のレンダリング開始前にメインクエリの内容を置き換えることができます。

しかし次の理由から、このquery_posts()を用いたメインクエリの直接の操作は避けてください

  • 後続の処理、特にプラグインでの処理に大きく影響し、不具合が発生する可能性がある
  • データベースからの再取得が発生するので効率が悪い

メインクエリの書き換えがどうしても必要な場合は、wp_reset_query()でメインクエリを復元しましょう。

なお、get_posts()ならメインクエリは置き換えられません。

注意: グローバル変数への直接アクセスは避ける

注意書きだらけですが、プログラミングでの一般的な注意である「グローバル変数を避ける」はWordPressでも同じです。

具体的には、PHPのglobalキーワードでグローバル変数をインポートしたくなる誘惑に負けないようにしましょう。グローバル変数に直接アクセスすると、以後のメンテナンスがさらに困難になってしまいます。

極力、pre_get_posts()などのフックを使ってください。

最後に

「Rails開発者向けのWordPress開発入門」は以上で終わりです。

より詳しくは書籍『エンジニアのためのWordPress開発入門』をご覧ください。


おたより発掘

関連記事

「徳丸浩とWordPress セキュリティを学ぶ夜」に参加いたしました

WordPressをローカル環境等にお引っ越しする方法(XAMPP使いも歓迎)


TechRacho編集部だより: 2018年を振り返る

$
0
0

こんにちは、hachi8833です。TechRachoをお読みいただいている皆さま、いつもありがとうございます。年末らしく振り返ってみたいと思います。

TechRachoの2018年

2018年のTechRachoでは以下の動きがありました。

今年後半で翻訳記事の割合が減ってきたことにお気づきの方もいらっしゃったかと思います。オリジナル記事は翻訳記事よりも手間がかかり緊張も高いのですが、ペースを掴むべく模索しています。

これからはときどき、こういう独自性の高い記事(≒中の人の体験や考えをメインに据えた記事)も増やしていくと、もっと魅力が増すんじゃないかなーと思いました。
そう簡単にブログはバズらない - give IT a tryより

今後の企画次第ですが、来年も当面はオリジナル中心路線で行くと思います。どうぞよろしくお願いします。

アドベントカレンダー2018

2018年のアドベントカレンダー企画を含めて今年TechRachoに記事を書いてくれた社内メンバーに感謝を込めて、TechRachoロゴ入りLAMY Safari L318ボールペン(ブラック)を配布しました🎁。参加いただいた皆さまありがとうございます!🙇

morimorihogeさんが文具愛を発揮して選びぬいた品です。

ひととおり配って私も初めてやっと手にしてみると、吸い付くような感触も書き味も上々です😋。ブランド音痴の私ですが、その私にもよさが伝わってくる一品でした。こればかりは手に取らないとわからないと痛感しました。

yancan.fm Podcastにて

上のツイートが目に止まって、このPodcastを急ぎチェックしました。恥ずかしながら、このときまでyancan.fmを知りませんでした💦


同Podcastより

「(好きなブログの話題)TechRacho… これ何て読むのw テックラコ?テクラコ?テックラチョ?」「これすごく好きで、Rubyの話しかしないんだけど」「後ははてブで追ったりとか」
同Podcastより(01:07:06のあたり): 大意

聞いてみたら出演者が全員女性で、一同がキャッキャウフフしながらTechRachoを話題にしているのが何とも不思議な心持ちでした❤。ありがとうございます🙇

ちなみに読みは「テックラッチョ」ということになってますが、実は社内でも読み方はさほど気にされていなかったりします。

TechRachoがテレビ進出?

少し前のことですが、突如「某TV番組にTechRachoステッカー入りのMacbookが映っていた」という情報が入ってきました。私は民放をほぼまったく見なくなって久しいので、自分ではまず気づけなかったと思います。

どうやらこの回だったようです↓。一時期公式動画が公開されていましたが現在は終了しています。こちらの動画は公式ですが予告ダイジェストなのでここにはMacbookは映っていません。

どうやらBPSの関係者がたまたま店に居合わせてマツコに軽くいじられたという流れだったようです😆

2018年度のTechRachoをお便りで振り返る

「読んでます」「読んでました」

Rails Developers Meetup 2018 Day 4 Nouvelle Vagueで私が配信でお手伝いした際にカルパスさんに大変お世話になりました。お疲れさまでした!

また読んでいただけるよう精進します。


「インプットも大事」、つまり「アウトプットも大事」ということですね😃


😂😂

「Web開発環境をMacbook ProからWindows機に移行してみた話」

morimorihogeさんのこのアドベント記事は今年圧倒的なアクセスを集めました。

「Railsウォッチサマリー」

Railsウォッチサマリーは新しい試みです。月刊化を目指して頑張ります!

RSpecの話

@junchitoさんに反応をいただきました。みんなテストで悩んでることがわかってくると不思議な連帯感を感じてしまいますね。

「モダンな開発用ターミナル環境のためのツール紹介」

TechRacho登場2回目のjhondaさんのアドベント記事に多くのアクセスをいただきました。私もripgrepを常用しています。rgという打ちやすい2文字なのも最高です😋

「Service Objectがアンチパターンである理由とよりよい代替手段(翻訳)」

大きな反響をいただきました。

クラスの置き場所に悩まずに済めば一番いいのですが😅

「自然言語学からみたプログラミング言語」

言語を愛するOasistさんの初アドベント記事がMatzにツイートされていました。おめでとうございます🎉

締めくくり

8月から試験的に開催した週刊Railsウォッチの公開つっつき会は、おかげさまで順調に開催を継続できるようになりました。私たちにとっても、読者の皆さまの生の声や関心事・悩みポイントに触れられる貴重な会となっています。

RailsdmでTechrachoの裏舞台を発表して以来、特に週刊Railsウォッチに反応をいただけることが増えてきました。こうした反応こそ私たちの糧とも言えます。


もちろんフィードバックもいただきました。紙面の品質向上にお力をいただきありがとうございます🙇

週刊Railsウォッチはもちろん、TechRachoのコンテンツはいずれも私一人の力では到底なしえないものです。morimorihogeさんをはじめ、つっつきや社内レビューに参加いただいている社内の皆さま、そして公開つっつき会にご参加いただいた方々、カンファレンスや外部勉強会でお世話になった皆さま、そしてTwitterやはてブで反応をくださった皆さまに改めてお礼を申し上げます。

TechRachoでは、皆さまからのTwitterやはてブなどでのご意見・ご感想を歓迎いたします。お気軽にどうぞ!

来年もTechRacho、週刊Railsウォッチ、そしてBPS株式会社をよろしくお願いします。

それでは皆さまよいお年を🎍

関連記事

【お知らせ】毎月第一木曜日は週刊Railsウォッチ「公開つっつき会」を開催します【定例】

年末特集: TechRachoの2017年度人気記事リスト

週刊Railsウォッチ(20190107)Railsのパフォーマンス改善Tips集、Rubyの`&:シンボル`ほか

$
0
0

あけましておめでとうございます。hachi8833です。今回のつっつきは年末に行われました。

  • 各記事冒頭には⚓でパーマリンクを置いてあります: 社内やTwitterでの議論などにどうぞ
  • 「つっつきボイス」はRailsウォッチ公開前ドラフトを社内有志でつっついたときの会話の再構成です👄
  • 毎月第一木曜日に「公開つっつき会」を開催しています: お気軽にご応募ください

狙ったわけではありませんが日本語記事が多めになりました。英語techニュース系サイトも年末ムードでまとめ記事が多そうです。

⚓新年: 週刊Railsウォッチ「公開つっつき会#6」開催のお知らせ

次回の新春公開つっつき会は1/10(木)に開催いたします。引き続きご応募をお待ちしております🙇

⚓Rails: 先週の改修(Rails公式ニュースより)

今回はコミットリストから見繕いました。

⚓Action Mailboxのコミットが導入

9月のDHHコミットがずらっと並んでいてギョッとしましたが、これだったんですね。


同PRより


つっつきボイス:「Action Mailboxは先週話題にのぼったRails 6の新機能ですね」「今週のコミットの大半はAction Mailboxでうずまってる感じでした」「コミット212件とかスゲー😆」「丁寧に追えばDHHがどんなふうにコードを書いているかわかりそうですね」「Basecamp社内でドッグフードしてたのがどどっとやってきた感じ」「デカすぎて全貌は追いきれない😅

「amとかacとか何かと思ったらActive ModelとかAction Cableとかか↓」「さすがにここまで略されると意味不明ですね😅」「asがActive Supportとかヤバい😆: こういうのが直ったのはうれしいし、フルネームで書くのがダルかった気持ちもわかる」

# .travis.yml#L58
  matrix:
-   - "GEM=ap,ac"
-   - "GEM=am,amo,as,av,aj,ast"
-   - "GEM=as PRESERVE_TIMEZONES=1"
-   - "GEM=ar:sqlite3"
+   - "GEM=actionpack,actioncable"
+   - "GEM=actionmailer,activemodel,activesupport,actionview,activejob,activestorage,actionmailbox"
+   - "GEM=activesupport PRESERVE_TIMEZONES=1"
+   - "GEM=activerecord:sqlite3"
+   - "GEM=guides"
-   - "GEM=ac:integration"
+   - "GEM=actioncable:integration"

「コンポーネントがひとつガツンと入った感じか: ビューまであるし、ちゃんと見ないとわからないけど、もしかするとAction Mailboxはマウンタブルエンジンとして導入されたのかな?」「不要だったらオフにできるといいですね」「Railsの他のコンポーネントにもオフにできるものがあるし、できるんじゃないかなー🤔

⚓フォームビルダーのAPIドキュメントを追加

# actionview/lib/action_view/helpers/form_helper.rb#L1683
      ##
      # :method: text_field
      #
      # :call-seq: text_field(method, options = {})
      #
      # Wraps ActionView::Helpers::FormHelper#text_field for form builders:
      #
      #   <%= form_with model: @user do |f| %>
      #     <%= f.text_field :name %>
      #   <% end %>
      #
      # Please refer to the documentation of the base helper for details.
...

つっつきボイス:「これはy-yagiさんのコミットで、今のままだとAPI Docがなくて不便だから追加したということみたいです」「ドキュメントが自動生成される?」「でしょうね: これはRailsガイド向けかな?API向けかな?: 見た感じYardでもRDocでもないように見える🤔」「そういえば独自っぽい」「そういえばRailsガイドにAPI Docの書き方が載っていた覚えが↓」「実際のAPI Docとはまた別っぽいしなー」

参考: API ドキュメント作成ガイドライン | Rails ガイド

⚓ルーティングテストからFacebook認証を削除

# actionpack/test/controller/routing_test.rb#L
  def test_route_with_regexp_for_action
    rs.draw { ActiveSupport::Deprecation.silence { get "/:controller/:action", action: /auth[-|_].+/ } }

    assert_equal({ action: "auth_google", controller: "content" }, rs.recognize_path("/content/auth_google"))
-   assert_equal({ action: "auth-facebook", controller: "content" }, rs.recognize_path("/content/auth-facebook"))
+   assert_equal({ action: "auth-twitter", controller: "content" }, rs.recognize_path("/content/auth-twitter"))

    assert_equal "/content/auth_google", url_for(rs, controller: "content", action: "auth_google")
-   assert_equal "/content/auth-facebook", url_for(rs, controller: "content", action: "auth-facebook")
+   assert_equal "/content/auth-twitter", url_for(rs, controller: "content", action: "auth-twitter")
  end

つっつきボイス:「Facebook-freeっていうからぱっと見Facebookがキライなのかと思った😆」「Twitterならいいのかと🤣」「珍しくサムダウン👎がある」「ポリティカルコレクト云々というより宗教的な話っぽい雰囲気?: Facebookってアカウントがないと見ることもテストもできないから、それを敬遠してるんじゃないかしら」「ぶっちゃけ積極的な意味はないし自分的にはどうでもいいし😆: サムダウンも『本質的でないコミットは入れるな』的なものなのかも」

参考: Become A Facebook-Free Business – Signal v. Noise

  1. 私たちはFacebook/Messenger/Instagram/WhatsAppの広告を購入しません。
  2. 私たちはFacebook/Messenger/Instagram/WhatsAppで宣伝もしくはビジネス活動もしくは顧客とのやりとりを行いません。
  3. 私たちはFacebookのいいねボタンやFacebookログインの提供を介してFacebookのデータ収集体制を支援することはしません。
    同ブログより

直接関係ありませんが、smoking-freeが「喫煙自由」と誤訳されるのは定番ですね。

参考: 毎日Eトレ!【145】知らないと危険! Smoking Freeは喫煙してもいい?

⚓Rails 6で必須になるRuby 2.5のFrozenErrorに対応

# activerecord/test/cases/aggregations_test.rb#L28
  def test_immutable_value_objects
    customers(:david).balance = Money.new(100)
-   assert_raise(frozen_error_class) { customers(:david).balance.instance_eval { @amount = 20 } }
+   assert_raise(FrozenError) { customers(:david).balance.instance_eval { @amount = 20 } }
  end

つっつきボイス:「なるほど、ビルトインのFrozenErrorが使えるようになったからそっちに変えようという話」「上の2つ目の古いコミットはfrozen_error_classが一時的に導入されたときのですね」「バージョン互換のために独自のfrozen_error_classを使っていたのね」

参考: class FrozenError (Ruby 2.6.0)

Object#freezeされたオブジェクトを変更しようとした時に発生します。
docs.ruby-lang.orgより

⚓不要なメソッドを削除

# actionpack/test/controller/rescue_test.rb#L65
- rescue_from ActionView::TemplateError do
-   render plain: "action_view templater error"
- end

- rescue_from IOError do
-   render plain: "io error"
- end

つっつきボイス:「これもy-yagiさんの年末大掃除っぽいコミット」「b00mとか謎すぎるメソッド名」「何だろね😆テストコードの消し忘れっぽいけど」「GitHubで追えるかな…GitHubではコミットidで検索できなかったorz」「全部のコミットidをチェックアウトできるようにするとめちゃ重くなりそうではあるけど: でもGitLabだとできた気がするなー」(つっつきの合間にいろいろ試した結果)「うまく見つからん😢」「とりあえず次いきましょー」

# actionpack/test/controller/rescue_test.rb#L354
   def b00m
      raise "b00m"
    end

GitLabだとできるようです↓。

参考: Allows to search within project by commit’s hash #24833 (!8028) · Merge Requests · GitLab.org / GitLab Community Edition · GitLab

⚓roleがない場合のエラーをわかりやすく改善

# activerecord/lib/active_record/connection_handling.rb#L160
    def with_handler(handler_key, &blk) # :nodoc:
+     unless ActiveRecord::Base.connection_handlers.keys.include?(handler_key)
+       raise ArgumentError, "The #{handler_key} role does not exist. Add it by establishing a connection with `connects_to` or use an existing role (#{ActiveRecord::Base.connection_handlers.keys.join(", ")})."
+     end

      handler = lookup_connection_handler(handler_key)
      swap_connection_handler(handler, &blk)
    end

つっつきボイス:「roleの話なのでこれもマルチDBがらみっぽいですね」「そういえば前は標準エラーで済ませてたような気がする」「connected_toのインターフェイスはどうやら今の感じで落ち着くのかな?」

# activerecord/test/cases/connection_adapters/connection_handlers_multi_db_test.rb#L332
+     def test_calling_connected_to_on_a_non_existent_handler_raises
+       error = assert_raises ArgumentError do
+         ActiveRecord::Base.connected_to(role: :reading) do
+           yield
+         end
+       end

+       assert_equal "The reading role does not exist. Add it by establishing a connection with `connects_to` or use an existing role (writing).", error.message
+     end
+   end

⚓Rails

⚓Railsのパフォーマンス改善Tips集(Ruby Weeklyより)


つっつきボイス:「パフォーマンスの話をするときにちゃんと測定の話から始めているのはいいな」「ビューのフラグメントキャッシュ、DBのカウンタキャッシュ、HTTPのetagとlast_modifiedと」「このetagって何でしたっけ?」「これはHTTPのレスポンスに付けられるetagで、リクエストにこのetagを含めて送信してetagが変わっていなければレスポンスを全部返さなくてもよくなるWebの標準機能😎」「そうでしたか😅

参考: HTTP ETag - Wikipedia

ETag: “686897696a7c876b7e”
Wikipediaより

「ActiveStorageを使うとパフォーマンスがよくなるというのはまた別の話の気がするけどなー?」

「HTTP/2を使えというのは納得」

「HTTP/2をCDNで使うのは不便という話: そもそもCDNってあんまりHTTP/2に対応していなかった気がするし、今のCDNでHTTP/2を使うと逆に遅くなるんじゃなかったかなー🤔」「おー?」「おそらくですが、今のインターネットではHTTP/2のように1つのTCPセッションですべてをやりとりするより、従来のように複数のTCPセッションを開く方が帯域を広く取れる気がする」「ふむう」

「Brotliって初めて見るけど何だろう?」「Gzip vsとあるから圧縮アルゴリズムか↓」「発音わからない😅

参考: Brotli - Wikipedia

「へー、Brotliって最近のブラウザで使えるのか↓: まあGzipがいにしえの圧縮アルゴリズムだし😆、新しい方がいいのかも」

Can I Use brotli? Data on support for the brotli feature across the major browsers from caniuse.com.

Brötliは「パン」「(ハンバーガーの)バンズ」のことみたいです。

参考: Spanisch Brötli - Wikipedia

「Heroku vs Bare Metalはそのとおりで、Herokuを大規模案件で使うとめっちゃハマるし☺☺」「やっぱりー」「記事にもあるけど、Herokuはさくっとサービスを立ち上げるにはいいけど、パフォーマンス面ではよい選択肢とは言えない: おっしゃるとおり」「この間のRailsdmでもそんな話があった気がしました」

「やる前から十分予測できることなのに、ハイパフォーマンスを要求する案件にHerokuを使うというのは、正直インフラの見通しが甘いと言わざるを得ない🧐」「Herokuで最大に課金して操縦桿をめいいっぱい引いてもダメですか?」「もう全然足りないですね😇: そのためにもシステム構築の段階でインフラエンジニアに参加してもらうべきだし、その費用をケチると後々泣きを見ます」「でしょうね😅

Resource Hints: prefetchって最近見かける」「prerenderとかpreconnectなんてのもあるのか!」「ブラウザによってサポートまちまち…」「Resource Hintsを使うとしたら、もうミリ秒単位で速度を追求するときでしょうね」

<link rel="prefetch" href="(url)">
<link rel="prerender" href="(url)">
<link rel="preconnect" href="https://example-domain.com/">

参考: Resource Hints: prefetch
参考: Resource Hints: prerender
参考: Resource Hints preconnect

「記事は下の方に行くほど改善幅が小さくなるっぽい」「その分下の方にいくほど機能も新しい感じですね😋

⚓SSRFとは


つっつきボイス:「徳丸先生の記事です」「Server Side Request ForgeryがSSRFか: CSRFになぞらえた感じの命名」

参考: クロスサイトリクエストフォージェリ - Wikipedia
参考: サイボウズ脆弱性報奨金制度で認定されたSSRF

「SSRFはおそらく概念としては前からある感じなんだろうけど…あーなるほど、これはもうおっしゃるとおりとしか」「というと?」

ここまで説明したように、任意のURLを対象とする処理はSSRF攻撃を受けやすく、また完全な対策は難しいのが現状です。
言い換えれば、「完全な対策が難しい」からこそ、今SSRFが注目されているとも言えます。
そもそも任意URLを受け取る処理が必要かどうかという仕様面の検討をした上で、実装の際にはできるだけ安全側に倒した処理と、アプリケーションとネットワークの両面からの対策を推奨します。
blog.tokumaru.orgより

「EC2には、URLを使って自分のインスタンスの情報(インスタンスメタデータ)を取れるという機能があるんですが、ここで例に使っているはてなブックマークみたいに、任意のURLを受け取ってそのコンテンツを取り出すサービスなんかだとこうやってEC2のメタデータを取ることができてしまう↓」「どっひゃー😱


blog.tokumaru.orgより

「メタデータを抜かれてどのぐらいヤバいかだけど…起動の変数とかcredentialとか抜かれたらエグいな😇」「ですね😢」「これは知らないと防げない脆弱性: そしてインスタンスメタデータを取れるというのは、どちらかというとAWSの知識」

「対策といってもURLの検証で完全にカバーするのは難しいしなー: 実際記事にも書いてあるし、URLの検証は結構奥が深い」「ネットワーク的な保護も有効です、とあるけど今度はプロビジョニングで詰まることもありそうだし、痛し痒し😅」「何らかのバリデーションは必要そう」

「これは確かに知っておくべき脆弱性👍」「対策は大変なんでしょうか?」「わかってしまえば対策そのものは大したことはないですね: 知っておかないとハマるヤツ」「なるほど!」

⚓Yuguiさんが解説するソフトウェアの設計手法


つっつきボイス:「お、これははてブで上がってたgrpc-gateway記事」「RubyやRailsと直接つながっているわけではありませんが、設計の参考になりそうに思えたので」

意図せざる技術的負債は厄介の種ですが、返済計画を立てた上で負債を選択すれば初期のアイディア検証段階にかけるコストを抑えられます。
同記事より

「技術的負債も薄い層でやってる分には問題ないだろうし、今後いつ問題になるかという見通しがちゃんと立っていれば、返済計画を立てて負債を選ぶというのはいいと思いますね」「ふむふむ」「アプリ側のインターフェイスでgrpcも使いたいしRESTも使いたいしJWTも使いたいみたいなことになってきたら、それぞれで薄いミドルウェアを実装するよりもこうやって間にgatewayをはさんで全部やらせる方に切り替えるというアプローチは、実装の手間をトータルで減らす方法としては悪くないと思います🧐」「なるほど!」「もちろん集中によるリスクも考慮したうえで、ね」

⚓その他Rails


railsdm.github.ioより


つっつきボイス:「論理DHH氏って一瞬そういうハンドル名の人がいるのかなと考えちゃいましたが、次回のRailsdmにDHHがリモートで質問に答えるということでした☺: amatsudaさんが通訳で」「この通訳はRailsをわかっている人でないと務まらなさそう☺」「試験的にで構わないから、リアルタイム英語字幕サービスを使ってくれたらうれしい」「あれ、このタイムテーブルだとDHHの時間が5分しかないことにw」「ホントだ🤣」(注: その後修正されています)

参考: UDトーク | コミュニケーション支援・会話の見える化アプリ

「そして秒速さんと神速さんが横に並んでるの図↓😆」「それでk0kubunさんがこのツイートを↓😆


railsdm.github.ioより


⚓Ruby

⚓ko1さんとmameさんによるRuby 2.6解説


つっつきボイス:「これこれ、さすがコミッター🎊」「これで言い尽くされてる本命感」「Rubyはこういうコアな情報が日本語で出てくるのがいいっすね~😋」「ホントホント」「😋」「英語苦手な人にも読んでもらえるし」「このお作法も初めて知りました↓」

完全に余談ですが、Unicode の大文字・小文字の話題になると、「Dz」という字の話をするのがお作法です。これは D と z の 2 文字ではなく、D と z が合体した 1 つの文字です。こういう文字を、digraph、二重音字と言います。この文字には、大文字・小文字に加え、タイトルケース(先頭の文字だけが大文字)の 3 種類があります。
同記事より

「そういえば2.6.に入れたけどやっぱり外す方がいいかな〜みたいなmameさんのツイートをさっき見た気がする」「どれどれ」「あったあった🎯↓」「2.6と3.0のポリフィルが意図せぬ破壊的変更にー😢」「こんなコードはたぶん書かないけどっ😆

⚓Rubyの演算子優先順位でやんちゃする


同記事より


つっつきボイス:「動物イラストと関数型言語大好きのBrandon Weaverさんの記事ですが、&Proc coercion operatorと呼ばれているって初めて知ったので」「マジですか!😳」「proc化する演算子だからわかるけど」

そういえば技術用語じゃないcoercionは基本的にネガティブな意味ですね。

{名-1} : 強制、無理強い
{名-2} : 抑圧、支配
{名-3} : 強制力、支配力

「名前あったんだー」「Rubyでは一応こういうものにはひととおり名前は付いているはずですが、検索しにくいという😆」「ホントホント」「Rubyを知った頃、&でprocになるのって直感的でないなーって思った」「ホントホント」「慣れるまでは」

# 同記事より
[1, 2, 3].select(&:even?)

「それにしてもRubyはなぜ&でprocになるのかと😆」「ましてや&:シンボルという合わせ技になると余計考えちゃった」「そもそものselectはブロックを取るメソッドだからまあいいとしてもー」「きっと当時空いてた記号を使ったんではないかと🤣」「🤣

「もしやメソッド定義のブロック引数&が起源なんじゃ?」「そんな気がする!😤」「にしても&:の後ろがシンボルでないといけないし」「しかもこの&:の組み合わせに呼び名がないし(ないよね?)」「&:シンボルを組み合わせたときだけこういう挙動になるというのがまた直感的でないし😅」「🤣」「🤣」「さすがにもう慣れたけど☺、特に他の言語からRubyにやってきた人が最初にギョッとしそう」「でも慣れると便利なショートハンド」「それは確か」

「ただこのショートハンドは書くのは楽だけど、中ではprocが動いてるので、そこは意識しておかないといけない」「そうっそうっ😤」「&:なんちゃらをメソッドチェインしてさらに&:なんちゃらとかやるとprocオブジェクトが大量に発生するするので、コードレビューでも気をつけてないといかんヤツ😅

「procは速度的にも不利だし」「何と比べて遅いんでしょうか?」「もうprocするだけで何やっても遅い🐢」「上のコードはselect!じゃないからselectでprocオブジェクトを作るコストは確かに無駄: でもねー、こうやって書けるとキレイキレイって個人的に思っちゃう❤」「ちょっとわかるー: チェインしなければ気持ちイイ!」

「表記はこんなにシンプルなのに、予想を超えて重いのが&:シンボル」「めちゃくちゃデカいarrayに対して使うと詰んだり、ね☺」「詰む詰む😅」「Integerのarrayなのに何でこんなに遅いんだ?みたいな」

「そしておなじみ、↓シンボルなしの&渡し」「こっちはlambda作って渡すヤツ」「あんま使わないけど🤣」「カリー化星人にはなくてはならないヤツか🍛」「デバッグのときによくRailsコンソールでこれ使ってるのー🥰: ただproductionではやりすぎ感あるけど」「たしかに: 例外が起こったときにほぼ読めなくなるし😅」「その場で生成された一時オブジェクトの例外になるから、デバッガーシンボルに名前がついてないみたいな状態になっちゃう😭」「みんな名無しさんに🤡」「メソッドチェーンって基本デバッグがやりづらくなる😅

# 同記事より
[1, 2, 3].map(&do_both)
=> [6, 11, 16]

「Mfって何だ?」「Modifier Function?!」「どことなく数学用語の匂い」

# scalaの書き方
List(1, 2, 3).map(_ + 5)

「Rubyだとたしかこの_、直前に評価された結果が入るんじゃなかったっけ?」「そんな$で始まる特殊変数みたいな微妙なものが?」「その_は絶対使わないという宣言とかじゃなくて?」「Rubyにもそういうのがあるんだったかなー?🤔

[Ruby] Kernelの特殊変数をできるだけ$記号なしで書いてみる

「Stackoverflowにこんなのあったし↓」「ひえぇ🤪」「簡単にアクセスできない呼び出し元にこうやってサクッとアクセスできるという感じかな」「JavaScriptにもこんなのがあった気が😅」「仕事のコードでは書いて欲しくない😅

参考: ruby - Where and how is the _ (underscore) variable specified? - Stack Overflow

lambda { |x, x| 42 }            # SyntaxError: duplicated argument name
lambda { |_, _| 42 }.call(4, 2) # => 42
lambda { |_, _| 42 }.call(_, _) # NameError: undefined local variable or method `_'
lambda { |_| _ + 1 }.call(42)   # => 43
lambda { |_, _| _ }.call(4, 2)  # 1.8.7: => 2
                                # 1.9.3: => 4
_ = 42
_ * 100         # => 4200
_, _ = 4, 2; _  # => 2

「で、さっきのproc coercionの&:even?↓って、&:のどっちが先に結合するんだろうか?」「ここはASTを調べてみるか」

[1, 2, 3].select(&:even?)

require 'ripper'して)「&:even?:args_add_block:symbol_literalだから&はやっぱりブロックか」「ということは…スペース入りの& :even?とやってもASTがまったく同じだっ↓🥶」「ホントだ」「やべー」「やべー」

irb(main):007:0>  Ripper.sexp('[1, 2, 3].select(&:even?)')
=> [:program, [[:method_add_arg, [:call, [:array, [[:@int, "1", [1, 1]], [:@int, "2", [1, 4]], [:@int, "3", [1, 7]]]], :".", [:@ident, "select", [1, 10]]], [:arg_paren, [:args_add_block, [], [:symbol_literal, [:symbol, [:@ident, "even?", [1, 19]]]]]]]]]
irb(main):008:0>  Ripper.sexp('[1, 2, 3].select(& :even?)')
=> [:program, [[:method_add_arg, [:call, [:array, [[:@int, "1", [1, 1]], [:@int, "2", [1, 4]], [:@int, "3", [1, 7]]]], :".", [:@ident, "select", [1, 10]]], [:arg_paren, [:args_add_block, [], [:symbol_literal, [:symbol, [:@ident, "even?", [1, 20]]]]]]]]]
irb(main):009:0> 

「まあこうやってASTをチェックすればわかるというのがいいところ😋」「あーびっくりした」

⚓Pryでコピペしたい


つっつきボイス:「この辺は.pryrcで割とどうにかなりそう」「この人はtmuxのコンフィグも使ってますね」

⚓Ruby trunkより

⚓Date.parse('2018')がエラーになるなんて


つっつきボイス:「Date.parse('2018')と書いたらそうなりそうだし」「'2018'ってDateじゃないし😆」「ヨーロッパのdd.mm.yyyy形式とかその辺が絡んでるっぽい」「1.1.のどっちが月でどっちが日かとか😢

「むしろissueで参照されている以下の動画↓が気になりました」「コードもスライドもなしでタイムゾーンを10分間熱く語ってるっぽい」「動画じゃなくていいんじゃね?😆

⚓クラウド/コンテナ/インフラ/Linux/Serverless

⚓EKSが東京リージョンに


つっつきボイス:「そうそう、EKS来ましたね」「KはKubernetesなんですね」「そう、AWSのマネージドクバネテ⚓

「AWSのECSはあくまでクローズドなサービスだけど、EKSはKubernetesなので、EKSで動けばGoogleの同種のサービスであるGKEでも原理的には動く、はず🙃」「おー」「厳密にはロードバランサーとかの仕様が違いますが、Kubernetesのレイヤは同じはずなので」「AWSにEKS、GoogleにGKEができたことでロックインされずにやれそうなのはうれしい😋

⚓その他インフラ


つっつきボイス:「これもはてブから」「レイヤーゼロって物理層ですよね?」「いや、物理層よりさらに下です😎」「あ、そっちか!」「ビルとか土地とか立地条件とか」「↓こういうのがレイヤーゼロだそうです」「なるほどー」「レイヤーとは言わない気もするけど😆


同記事より

⚓SQL

⚓MySQL 8.0解説


つっつきボイス:「単なる機能紹介じゃなくて、割と運用面の具体的な注意点にも触れていてよさそうだったので」「ウィンドウ関数やっと入ったか」「おすすめはRANK関数…って、なかったの?」「そういえばRANK関数はじめて使ったのはぽすぐれだった」

参考: ウィンドウ関数

「そしてJSON機能と独特の記法↓…MySQLもとうとうJSON地獄に仲間入り👹」「きた〜」「きた〜」「こないだPostgreSQLでJSON地獄見た〜」

-- 同記事より
SELECT 
    doc ->> '$.firstname' AS firstname,
    doc ->> '$.lastname' AS lastname,
    doc -> '$.score' AS score
FROM
    students
WHERE
    doc->'$.score' > 80;

「リソースグループは、MySQLをカリカリにチューニングしたい人によさそう」

「お、認証プラグインがcaching_sha2_passwordに変わるのか」「しゃーつー」「古いMySQLクライアントからだと8に接続できなくなると: これは知らないとハマる😅

「え、クエリキャッシュ消えるの?」「一度入ったのに消える?」「クエリキャッシュはかなり昔からあって、しかもよく使われてました😭」「あ、記事にもあるけど5.6からデフォルトで無効化されていたかも」

log_binのデフォルトが1になった: これは注意しないとディスクがあふれる🚰」「log_binはバイナリログなんですが、トランザクションログというか実行された更新系SQLがすべて入っているのですんごい量になります」「ひょえー」「逆に言うとlog_binがあればデータを巻き戻せる」「おー」「AWSだとデフォルトで8GBぐらいだからlog_binで割と簡単に死ねます😇」「記事には大丈夫ですよって書いてるけど」「どうかなー😆」「用途とかlog_binのローテーション設定にもよるけど、でかいバッチをぶん回したときとか危ないかも」「log_binを文字列で出すと容量が増えるので注意⚠

⚓ぽすぐれびとに聞きました: お気に入りのツールや技は?(Postgres Weeklyより)

このgemはRubygemではありませんでした(´・ω・`)。


つっつきボイス:「ぽすぐれは知られざる機能がもう多すぎて😆」「この間のcitextもある」「psql -xとか」「row_number() over()とか」「FDWとか」「GIN Indicesとか」「この記事↓にあったヤツですね」「psqlでvimも使えるのか」「Unix的に普通に使えそうですね☺

PostgreSQLの機能と便利技トップ10(2016年版)(翻訳)

「pl/brainfuck😆」「マジで😆」「brainfuckでストアドプロシージャ書くやついるのか😆

参考: Brainfuck - Wikipedia

「ぽすぐれはこういう機能がいろいろあっていいですね〜😋

⚓JavaScript

⚓TypeScript Deep Dive


つっつきボイス:「これもはてブから」「もうそろそろ、TypeScriptがブラウザでそのまま動くようになればいいのに」「そうっそう思うっ👍」「デバッグのときにTypeScriptがコンソールで動いたらどんなにいいかと😂」「こっそりでいいから入れといて欲しいの😍」「Googleならやる😆

⚓その他JavaScript


つっつきボイス:「おー、ブラウザだけじゃなくてNode.jsとかのHTTPリクエストを扱うライブラリか」「Fetchもあるし」

⚓CSS/HTML/フロントエンド/テスト

⚓WindowsのIME切り替え


つっつきボイス:「この記事見たかも」「今英語キーボードだからもともと半角/全角キーないし😆

Macのコマンドキーだけ搭載したノートPCが欲しいのは私だけなのでしょうか。

⚓言語

⚓機械学習あるある


つっつきボイス:「これはいろいろわかりみ☺」「わかりすぎる😆」「新しいものが出てくると割とこういう道を辿る感じですね」 「Railsもある意味似たような道を辿ってるし: 『Railsなら何でもあっという間に作れます』とか😆」「全部scaffoldでやるとか😆

⚓その他言語


つっつきボイス:「これもすっごくわかりみ〜」「かと思うと、あえてこの時期の古い情報が欲しい、というのもある😎」「これってRails 3ではどう書くんだっけ?とか」「たしかにー」


⚓その他

⚓コンクリートの平成史


つっつきボイス:「これもはてブで見た」「ジャンルは違うけどエンジニア記事としていいなと思って」「視点が割と固定気味かなとも思うけどいい感じの記事👍」「どの世界でも専門家の話はやっぱり面白い😋

「これで連想したんですが、ちょっと前に自宅付近の道路でアスファルト張替え工事が終わったら、自動車の走行音がびっくりするほど静かになってたので(体感で1/3ぐらい)、きっとこういうの↓をやったんだろうなと思って」

⚓社名変更


つっつきボイス:「ダイナブックといえば大昔にアラン・ケイが提唱した『ダイナブック構想』↓でしょう」「私もー」「ダイナブックという名前もだいぶくたびれてきた感」

参考: ダイナブック - Wikipedia

「シャープといえばMURAMASAっていう当時としてはうっすいノートPCがありましたね」「あったあった!」「知らなかった〜」「↓記事の写真はまだもりっとしてますね」「これじゃなくてA4サイズの大きいヤツだったはず」

参考: 【モバイラーが憧れた名機を今風に蘇らせる】シャープ「MURAMASA PC-CV50F」 ~暗黒時代を生き抜いたEfficeon搭載小型モバイル - PC Watch

「当時はこれちょっといいなって思ったことあり😋」「ネーミング的にそっちの路線の方が好き😍: スズキのカタナみたいで」「名前負けしたらそれはそれで恥ずかしいかも😆」「こうなったのはシャープがチャイナブランドになったからなのかなー」「チャイナブック😆」「😆

⚓群衆の英知もしくは狂気


同サイトより

今日の社内勉強会で紹介されていました。


つっつきボイス:「これも実はちょっと前にはてブでバズってましたね」「勉強会の後Macbookからずっと音がしててどのタブだろうと思ったらこのサイトでした😅

⚓番外

⚓ロボット手術@築地


つっつきボイス:「リモート手術は日本でもだいぶ始まってますね: しかも海外だとさらに普及してるし」「実用性はクリアしていて、後はコストだけだって記事にもありました」「さすがに全自動ではない?」「ゲーセンのコントローラみたいなので操作するみたいです: 手ブレとか危険な動きを完全にキャンセルしたり」


今回は以上です。

今週の主なニュースソース

ソースの表記されていない項目は独自ルート(TwitterやRSSなど)です。

Rails公式ニュース

Ruby Weekly

Hacklines

Hacklines

Postgres Weekly

postgres_weekly_banner

Webのバッチ処理とオンライン処理のポイントとシステムの応答性能を学ぶ#2(社内勉強会)

$
0
0
  • #1 処理時間とレスポンス時間
  • #2 バッチ処理とオンライン処理(本記事
  • #3 バッチ処理を設計するときの注意点
  • #4 オンライン処理とUXの工夫
  • #5 Railsのジョブ管理システムと注意点
  • #6 バッチ処理ですぐに使えるノウハウ、まとめ

3. 「バッチ処理」と「オンライン処理」の違いを理解する

バッチ処理とオンライン処理は対になる考え方です。

バッチ処理

ある程度の量のデータをまとめて処理することを「バッチ処理」と呼びます。「一括処理」と呼ばれることもあります。

バッチ処理はある程度(数分〜数時間)時間のかかる処理に用いられるため、案件にもよりますが、一般にある程度長めの処理時間が許容されます(もちろん顧客への説明と了解も必要です)。計算量の多い処理や、日次/月次/年次といった締め処理は多くの場合バッチ処理で行われます。

「数時間かかるバッチなら営業時間が始まるまでに終了して欲しい」「1時間に1回実行するバッチなら数分または数秒以内で終わって欲しい」などがよくあるパターンです。

バッチ処理の場合、実行結果や経過の表示はシンプルにログファイルや終了時通知を用いることがよくあります。その分ユーザーにとっては見えにくいものになります。

morimorihoge注)
バッチ処理で最も一般的なのはCronなどによる定時実行(毎日1時に実行、など)です。
複雑な要件になってくるとジョブネット(次回以降解説予定)などの待ち合わせ処理も出てきたりしますが、どうしても必要な場合以外はあまりバッチの実行条件を複雑にしない方が良いことが多いです。
なぜなら、バッチ処理は大抵とても大事な処理を扱うので、複雑であることはそのままオペレーションミスに繋がる可能性があるからです。

典型的なユースケースとして「毎日の注文一覧を集計してメールする」というバッチ処理があった場合「注文が0件だった場合にはメール送信しない」という条件をつけると一件管理者にとって便利に思えますが、この仕様にしてしまうと**バッチ自体が失敗したのとの区別がつかなくなってしまいます**。
こうした「うまく動かなかったときに大きな問題となる機能」についてはオンライン処理でも同じですが、多少慎重すぎるくらいにログや通知を設計しておくのが良いでしょう。

オンライン処理

ユーザーからのリクエストをその場で処理して処理結果を返すことを「オンライン処理」と呼びます。情報処理試験などでは「対話型処理」という用語が使われることもあります。

ユーザーは結果が返されるのを待つため、短時間で処理を終えることが求められます。Webアプリの場合、ほとんどのユーザー操作がこのオンライン処理に相当しますので、処理に時間がかかるとユーザーエクスペリエンスが低下します。

現在はコンピュータの速度が向上したこともあって、Webアプリに限らず、昨今はほとんどの処理をオンライン処理として実装することが求められるようになっています。

なお、コンピュータサイエンス(CS)系のOS分野では、リアルタイム処理という用語を厳密に「実時間処理」という意味で使います。その文脈では、単に応答が速いだけでは多くの場合リアルタイム処理とは言えず、たとえば「この処理は1msec以内に必ず応答を返す」というように応答時間の保証を伴います。

morimorihoge注)
僕はOS系の研究室にいたのでどうしてもオンラインリアルタイム処理をリアルタイムと略すのに抵抗を感じてしまいます 💦
# 「Linux」じゃなくて「GNU/Linux」と呼べ!みたいなもんです。多分

補足「非同期処理」とは

SPA(シングルページアプリケーション)などで多用される非同期処理は、バッチ処理とオンライン処理の両方の特徴を備えています。

ここでいう同期とは「(処理の完了を)待つ」ということです。つまり非同期は「処理の完了を待たずに、相手に任せて次の処理に進む」ということになります。

Railsでは、バックグラウンドジョブの非同期処理のためにRedisSidekiqなどを使うこともよくあります。
※非同期処理の終了確認の方法が必要で、RedisにはPush通知機能(Pub/Sub)があります

たとえばAjaxによるブラウザ操作の非同期処理は、ユーザー側から見ればすぐ次の操作を行えるのでオンライン処理的ですが、サーバー側にとっては呼び出されたした別リクエストは裏でバッチ的に処理されるので、両方のいいとこ取りとも言えます。しかし非同期処理を実装するには、オンライン処理とバッチ処理の両方を理解する必要がありますし、Ajaxを含む非同期処理は難易度も上がり工数も増えることに留意しましょう。

morimorihoge注)
非同期処理は正常系だけ見ればどうってことないのですが、例外処理を考え始めると途端に考えることが増えます。
代表的なものだけでも以下のようなものがあります。

* ユーザーが非同期処理の実行中にブラウザウィンドウを閉じてしまったら処理結果はどうする?
* バッチ的な処理の場合、同じ処理を複数(または同じ)ユーザーが同時に実行してしまったらどうする?(定時バッチと違い、複数同時実行のケアが必要)
* 長い非同期処理を走らせる場合、非同期処理のWorker数やタイムアウトの設定はどうする?(長い処理が他の非同期処理をブロックしてしまう可能性がある)
* 非同期処理の実行状態をどのように管理する?エラー処理は?

ここに挙げたものはサーバー側の非同期処理の話ですが、フロントエンド側もSPAになるとフロントエンド側の非同期処理(Service Worker利用など)も出てきてさらに複雑になってきます。
基本的には非同期処理よりも同期処理の方がシンプルで考えることが少なく、バグも作り込みにくいです。初心者であれば、まずは多少動いてなくても影響の小さそうな処理から非同期処理の実装を初めて見るのが無難かと思います。

「バッチ処理」と「オンライン処理」のどちらを選ぶべきか

戦略としては、基本として「オンライン処理」を選択します

Webシステムでは「何か操作を行ったらすぐに反応を得られる」というユーザーエクスペリエンスが求められるのが普通ですし、オンライン処理ではリクエスト/レスポンスの中ですべての処理が完結するのでバッチ処理に比べてずっとエラーを追いかけやすいなど、メリットが圧倒的に大きくなります。

たとえばWebアプリの入力フォームから確認フォームへの遷移でエラーが発生したのであれば、どこでエラーが発生したのかがすぐ見当が付きます。
バッチでエラーが発生した場合、たとえばバリデーションのエラーなのかコミット時のエラーなのかを調べるにはコードやログを追いかける必要があります。

バッチ処理は、オンライン処理では実現が難しい場合にはじめて検討します。

  • オンライン処理を高速化するための事前処理
  • オンライン処理では難しい大規模なトランザクション処理(伝票の月次締め処理など)
  • 外部APIとの接続にバッチ処理が指定されている場合
morimorihoge注)
この辺りは開発の経験をどのような環境で積んできたかによって、エンジニアによっても意見が分かれるところかと思います。
なんでもバッチ処理に寄せたがるエンジニアもいれば、バッチ処理でもよさそうなこと(定時処理など)をオンライン処理から実行させるような設計を好むエンジニアもおり、この辺りは価値観により千差万別です。

ここで挙げた「基本はオンライン処理」は恐らく一般的と思われる設計方針ですが、システムの性質、ユーザーの慣れているUI、フレームワークやライブラリとの相性などによってもどのやり方がベストになるかは変わります。
色々な選択肢がある中で、今開発しているシステムにはどのような設計が適してるのかを考えて選ぶ力がシステムアーキテクトには求められます。

4. 「オフラインバッチ」と「オンラインバッチ」

バッチ処理は、さらに「オフラインバッチ」と「オンラインバッチ」の2種類に分けられます。

(1)オフラインバッチ

オフラインバッチは最も直接的なバッチ処理の形態です。バッチ処理の間はオンライン処理を停止しておき、バッチがシステムリソースを安全に専有できるようにします。

オンライン処理に邪魔される心配がないため、安全かつ実装しやすく、実行時間も予測しやすいという大きなメリットがあります。

バッチの同時実行がない環境であれば、バッチ処理中のデータ競合を気にしなくてよいため、DBのロックをそこまで意識せずにバッチを書くことも可能です(失敗時ロールバックの考慮は必要)。ディレクトリの下の多数のファイルを書き込み/更新するなどの処理でも、作業中のファイルロックやファイルの追加を気にせずに済みます。

その代り、顧客がオンライン処理の停止を許容しなければこの戦略は使えません。昨今のWebサービスは年中無休で運用されることが増えているので、以前に比べてオフラインバッチは行いにくい傾向があります。オフラインバッチを許容してもらえる見込みがあるなら、頑張って交渉しましょう。

オフラインバッチはその時間サービスが止まるので、ユーザーにとって不便が生じますが、「メンテナンス時間」を設けることで安全にバッチ処理を行えるようにする戦略も検討の余地があります。

morimorihogeコメント)
例えばJR東日本のえきねっとは今でも(2018年11月)毎日[システムが一部利用できないメンテナンス時間](http://www.eki-net.com/top/jrticket/guide/reserve/timein.html)があります。20世紀のWebシステムではこうした毎日メンテナンスを行うシステムも多く、社会的に受け入れられていましたが、昨今ではこうしたシステムは「普通」ではなく「不便」と言われるようになりました。

堅い系システムの代表格である銀行系ですら、最近[全銀の他行振込が24時間に対応](http://www.itmedia.co.jp/business/articles/1810/10/news047.html)しましたので、システムに関する常識というのは変わっていくものだなあとしみじみ感じます(おっさん感

とはいえ、日本で営業時間にしか使われないシステムであれば今でもオフラインバッチは現役なので、システムの複雑さを下げる一つの手段としては手札に持っておくと良いでしょう。

(2)オンラインバッチ

オンラインバッチは上と対照的に、オンライン系を停止せずに実行します。

サービスが停止しない前提なのでユーザーや顧客にとってはメリットがあります。

その代りオフラインバッチに比べて考慮点が増えるため、難易度は上がります。バッチ処理中にもトランザクションや状態を綿密に管理しなければならなくなるため複雑になりますし、オンライン処理に影響されて実行時間も予測しづらくなります。

きちんと設計と、バッチ処理中に発生したデータやファイルの変更や増減を取りこぼす可能性があります。処理中に消されたファイルを開こうとしてエラーで落ちるというのはありがちなシナリオです。

なおWebでの非同期処理は、オンラインバッチの一種とみなすこともできます。

morimorihogeコメント)
処理時間が数秒程度と短いうちはオフラインバッチとオンラインバッチでそれほど実際の影響が出ることは少ないのですが、オンラインバッチは時間が長くなればなるほどデータ不整合が障害が発生する可能性が上がります。
また、運用開始時は処理時間が短くても、数年運用するうちに処理時間が長くなり、今までたまたま踏んでなかったバグを踏むようになるというケースもわりとあるあるな話なので、設計時には注意しましょう。

  • #1 処理時間とレスポンス時間
  • #2 バッチ処理とオンライン処理(本記事
  • #3 バッチ処理を設計するときの注意点
  • #4 オンライン処理とUXの工夫
  • #5 Railsのジョブ管理システムと注意点
  • #6 バッチ処理ですぐに使えるノウハウ、まとめ

関連記事

Railsのテンプレートレンダリングを分解調査する#2 ActionView編(翻訳)

$
0
0

概要

原著者の許諾を得て翻訳・公開いたします。

Railsのテンプレートレンダリングを分解調査する#2 ActionView編(翻訳)

本記事は、Railsのテンプレートレンダリングを分解調査する#1探索編の続きです(半年も前の記事で失礼!)。今回は、RailsがRubyオブジェクトからテンプレートをレンダリングする過程を解説します。

今回チェックするファイル

  • actionview/lib/action_view/renderer/template_renderer.rb
  • actionview/lib/action_view/template.rb

ここまでのおさらい

前回の記事では、Railsがテンプレートファイルを読み取り、それを用いてテンプレートオブジェクトを初期化するところまでをご紹介しました。

# actionview/lib/action_view/template/resolver.rb

def query(path, details, formats, outside_app_allowed)
  query = build_query(path, details)

  template_paths = find_template_paths(query)
  ......
  template_paths.map do |template|
    ......    
    contents = File.binread(template)

    Template.new(contents, File.expand_path(template), handler,
      virtual_path: path.virtual,
      format: format,
      variant: variant,
      updated_at: mtime(template)
    )
  end
end

Railsはテンプレートオブジェクトをどう使っているか

ActionView::TemplateRendererがテンプレートを見つけると、#render_templateを呼び出し、テンプレートを引数として渡します。このときにlayoutlocalsも同時に渡され、オプションから展開される点にご注意ください。

# actionview/lib/action_view/renderer/template_renderer.rb

module ActionView
  class TemplateRenderer < AbstractRenderer #:nodoc:
    def render(context, options)
      ……
      # Found your template
      template = determine_template(options)
      ……
      render_template(template, options[:layout], options[:locals])
    end
  end
end

そして以下が#render_templateです。

def render_template(template, layout_name = nil, locals = nil)
  .....

  render_with_layout(layout_name, locals) do |layout|
    # instrumenting block
      template.render(view, locals) { |*name| view._layout_for(*name) }
    # end
  end
end

実際のレンダリングがrender_with_layoutブロックでラップされていることがわかりますね。今回はこの部分をスキップしますが、また別のテンプレートレンダリング記事でご説明したいと思います。

Template#render

Template#renderでは2つのステップを実行します。テンプレートのコンテンツをメソッド(もちろんRubyのメソッドです)にコンパイルする作業と、そのメソッドを呼び出す作業です。これだけシンプルなら十分理解できますね。

def render(view, locals, buffer = nil, &block)
  ......
  compile!(view)
  view.send(method_name, locals, buffer, &block)
end

テンプレートの「コンパイル」とは

ここで言うテンプレートのコンテンツのコンパイルとは、RailsがActionView::Base(より正確にはActionView::CompiledTemplates)にメソッドを1つ定義することを指します。そのメソッドはlocalsを受け取って文字列を1つ返します。ここで例を用いて解説します。localを1つ受け取るsay_hiというテンプレートをレンダリングしたいとしましょう。

# say_hi.erbHi <%= name %>

テンプレートがコンパイルされてメソッドになると、以下のようにRailsで使えるようになります。

def say_hi(local_assigns)
  name = local_assigns[:name]
  “Hi #{name}”
end

view.say_hi(name: "Stan")
#=> Hi Stan

ここの仕組みはどうなっているのでしょうか?Railsはこれを実現するために、次のようにRubyのメタプログラミングサポートをフル活用しています。

require "erb"
# ViewはActionView::Base的に振る舞うクラス
class View
end

class Template
  def initialize(name, content)
    @name = name
    @content = content
  end

  def render(view)
    compile(view)
    view.send(@name)
  end

  def compile(view)
    # コンパイルが必要なのは1度だけ
    return if @compiled
    # テンプレートエンジンとしてerbを使う
    body = ERB.new(@content).src
    src = <<-end_src
      def #{@name}
        #{body}
      end
    end_src

    view.singleton_class.module_eval(src)

      @compiled = true
  end
end

view = View.new
template = Template.new("say_hi", "Hi!")
template.render(view) #=> Hi!
view.methods.first #=> :say_hi

しかしlocalsはどうやって処理しているのでしょうか?Railsでテンプレートごとに定義されるメソッドが1つだけだとしたら、どうやってlocalsを動的に渡せばよいのでしょう?私が最も見事だと思った設計はこの部分です。上のコードを少し変えてみましょう。

require "erb"

class View
end

class Template
  def initialize(name, content, locals)
    @name = name
    @content = content
    @locals = locals
  end

  def render(view, local_assigns)
    compile(view)
    view.send(@name, local_assigns)
  end

  def compile(view)
    return if @compiled
    body = ERB.new(@content).src

    src = <<-end_src
      def #{@name}(local_assigns)
        #{locals_code}
        #{body}
      end
    end_src

    view.singleton_class.module_eval(src)

      @compiled = true
  end

  def locals_code
    @locals.map do |local|
      "#{local} = local_assigns[:#{local}]"
    end.join("\n")
  end
end

view = View.new
template = Template.new("say_hi", "Hi! <%= name %>", [:name])
template.render(view, name: "Stan") #=> Hi! Stan

先の例で用いたsay_hiメソッド全体は、以下のように定義されます。

def say_hi(local_assigns)
  # locals_codeで生成される
  name = local_assigns[:name]

# 以下はERBで生成されるが、わかりやすくするため展開してある
  _erbout = +’’
  _erbout.<< “Hi! “.freeze
  erbout.<<(( name ).tos)
  _erbout
end

巧妙な動作だと思いませんか?私は何年も前にこの実装を初めて読んだときについ惚れ惚れとしてしまいました。そしてこの実装は今も変わっていないのです!

もちろん、実際の#compileメソッドはさらに複雑なケースに対応するため、これよりずっと複雑になっています。このあたりにご関心がおありの方は、こちらをご覧ください。

なお、以上のメカニズムはRailsコンソールを使って直接確認することもできます。

# 注: 以下の`posts`や`posts/index`は実際のRailsアプリのものに変更すること
paths = ActionController::Base.view_paths
view = ActionView::Base.new(paths)

# この時点ではコンパイル済みメソッドがない
view.methods.grep(/posts/) #=> []

# このrenderはaction_view/helpers/rendering_helper.rbが由来
view.render(template: "posts/index")

# コンパイル完了!
view.methods.grep(/posts/) #=> [:_app_views_posts_index_html_erb___4416254959938662165_70267190542480]

まとめ

ActionViewは実によく設計されたライブラリであると私は思っています。ActionViewによるテンプレートの探索やレンダリングの手法はエレガントです。そしてActionViewの基本設計はこの10年でほとんど変更されておらず、それでいて深刻な問題を引き起こしていないのです!私はActionViewのコードベースから実に多くのことを学びました。皆さんもお手すきのときにActionViewのコードを読んでみてはいかがでしょう。その価値は十分あると思います😋

関連記事

Railsのテンプレートレンダリングを分解調査する#1探索編(翻訳)

Railsのフラグメントキャッシュを分解調査する(翻訳)

週刊Railsウォッチ(20190121)Rails 6.0.0 beta1リリース、Railsは2019年も「あり」か、Jetsでサーバーレス、ES2018の新機能、RSpecの心ほか

$
0
0

こんにちは、hachi8833です。Slackのアイコンやら色合いやらが微妙に変わって社内がざわついていました。


つっつきボイス:「Slackのデザイン変更、はてブでもざわついてましたね😅」「このデフォルトアバター↓変えたくてしゃーないんですが、Slackのadminでも変えられないっぽい😢」「この英語圏っぽさ…か、可愛くない😆

「キャラクターの可愛さに対する感覚って、アジアの中でも違ったりするけど、これは少なくとも日本的ではないなー」「越えられない壁🗻」「そう思うとGitHubのOctocat(本当はMonalisa)は日本的ではないけど大当たりですね: そこまで深く考えずにデザイン発注したらしいと何かで見た気がしますが」「キャラクターはつくづく水物🛶

後で調べたらこの方↓が生みの親でした。

  • 各記事冒頭には⚓でパーマリンクを置いてあります: 社内やTwitterでの議論などにどうぞ
  • 「つっつきボイス」はRailsウォッチ公開前ドラフトを社内有志でつっついたときの会話の再構成です👄
  • 毎月第一木曜日に「公開つっつき会」を開催しています: お気軽にご応募ください

⚓臨時ニュース: Rails 6.0.0 beta1がリリース(Rails公式ニュースより)

⚓Rails: 先週の改修(Rails公式ニュースより)

最近のコミットではAction Cable、特にテストとガイドの改修が目立ちました。

⚓RelationのwhereでRuby 2.6の(1..) rangeをサポート

# activerecord/test/cases/arel/attributes/attribute_test.rb#L642
+       if Gem::Version.new("2.6.0") <= Gem::Version.new(RUBY_VERSION)
+         it "can be constructed with a range implicitly ending at Infinity" do
+           attribute = Attribute.new nil, nil
+           node = attribute.between(eval("0..")) # Use eval for compatibility with Ruby < 2.6 parser
+
+           node.must_equal Nodes::GreaterThanOrEqual.new(
+             attribute,
+             Nodes::Casted.new(0, attribute)
+           )
+         end
+       end

つっつきボイス:「おーRuby 2.6のinfiniteサポート😍」「これもRails 6で入るみたいですね」「Rails 6がRuby 2.6をサポートするようになった一環なんだろな」

参考: Feature #12912: An endless range `(1..)` - Ruby trunk - Ruby Issue Tracking System

relation.where(column: (1..))って、ぱっと見..の後ろを書き忘れたみたいに見えちゃいます😅: 慣れてないだけですが」「whereにはこうやって書けるみたいだけど、正直なところ、これを普通のrangeで使う機会はあまりなさそう&あまり使いたくないかなー😆」「ちょっとためらいます😅」「ただ言語表現として書けることについてはまあいいかなとも思いますし、この改修もそのぐらいの意図なのかなと」

1..って、むしろコードゴルフで少しでも短く書きたいとか、ぱっと見そう見えない無限ループを簡単に作っていたずらするとか、そういう方面をいろいろ考えちゃう😆」「わかりみ〜😆」「RubyKaigiで@mametterさんが主催してたTRICK 2018↓みたいな場面で使えそうだし」

その後

#34906の続きが入りました↓。

⚓WebワーカーでAction Cableを使ったときのReferenceErrorを回避した

# actioncable/app/assets/javascripts/action_cable.js
(function(global, factory) {
  typeof exports === "object" && typeof module !== "undefined" ? factory(exports) : typeof define === "function" && define.amd ? define([ "exports" ], factory) : factory(global.ActionCable = {});
})(this, function(exports) {
  "use strict";
  var adapters = {
-   logger: window.console,
-   WebSocket: window.WebSocket
+   logger: self.console,
+   WebSocket: self.WebSocket
  };

つっつきボイス:「Webワーカーがwindowdocumentにアクセスしようとするとエラーになってたそうです」「それはウィンドウがないからですね」「あ、そういうことか😳

「Action Cable使ってたらWebワーカーでwindowdocumentにアクセスするのはまったく普通だから、とっくに誰かが踏んでいてもおかしくないし、こういう基本的なエラーが今になって修正されてるということは、Action Cableってまだあまり使われてないのかな?🤔」「最近Action Cableの改修が多いのは、6.0ではもっと使ってもらおうみたいな流れなのかな?」「これは単に地雷を踏んだ人が改修したんではないかと😆

⚓ガイドの「Decoratorパターン」を既存クラスのオーバーライドに変更

# guides/source/engines.md#L1091
### Overriding Models and Controllers
Engine model and controller classes can be extended by open classing them in the
main Rails application (since model and controller classes are just Ruby classes
that inherit Rails specific functionality). Open classing an Engine class
-redefines it for use in the main application. This is usually implemented by
-using the decorator pattern.
+redefines it for use in the main application.

For simple class modifications, use `Class#class_eval`. For complex class
modifications, consider using `ActiveSupport::Concern`.

-#### A note on Decorators and Loading Code
+#### A note on Overriding and Loading Code

-Because these decorators are not referenced by your Rails application itself,
-Rails' autoloading system will not kick in and load your decorators. This means
+Because these overrides are not referenced by your Rails application itself,
+Rails' autoloading system will not kick in and load your overrides. This means
that you need to require them yourself.

つっつきボイス:「ガイドの修正ですが、Decoratorパターンは適切ではないということでオーバーライドに改めたようです」「自分はDecoratorキライだからまあどっちでもいいけど😆」「そうでしたね😆

参考: Decorator パターン - Wikipedia

「しかしoverrideというのもどうなんだろう?🤔: これでコミッターのコンセンサスを取れたのがちょっと不思議ではある」「これはこれで微妙なんでしょうか?」「お、コミットメッセージ↓にこう書いてあるし: それならDecoratorじゃないというのはワカル」

現行のエンジンガイドでは、Overriding Models and ControllersセクションでDecoratorパターンを参照しているが、Decoratorパターンは既存クラスをreopenするものではなく、既存オブジェクトのインスタンスに機能を追加するもの(RubyではよくDelegatorで実装されるヤツ)なので、自分には適切でないように思える。
同コミットより抜粋・大意

「Decoratorの図↓にもあるように、通常のGoFのDecoratorパターンではコンポジットを使うので、delegate的に書くのが本来のあり方と言える」「あー」「Rubyならこの辺はどうにでも書けますけどね☺


Wikipediaより

「そしてガイドのコードはクラスをreopenしているから、その意味では確かにDecoratorパターンではない」「なるほど!😃」「Decoratorパターンではないのは確かだけど、代わりにoverrides/というディレクトリ↓を作るのは自分的にどうも引っかかるな〜😅」「services/とかforms/みたいなディレクトリですね😆」「とはいえ、何かディレクトリを作ってカスタマイズできるよというサンプルは何らかの形でガイドにある方がいいですね: 自分もガイドでそういう書き方を参考にしたことがあるし」

-# MyApp/app/decorators/models/blorgh/article_decorator.rb
+# MyApp/app/overrides/models/blorgh/article_override.rb

「といってservices/をサンプルに使うとそれはそれで議論になりそうだから、サンプルにするならforms/あたりがいいんじゃないかと」「フォームなら利用頻度も高いですし😋

⚓Active Recordからいろいろと削除


つっつきボイス:「3つともkamipoさんのコミットでした」「1つ目と3つ目はprevent_writesattr_accessorからattr_readerに移したと: 書き込み可能で公開すべきではないからということですね」「2つ目は単なるリファクタリングか」

# activerecord/lib/active_record/connection_adapters/abstract_adapter.rb#L83
-     attr_accessor :pool, :prevent_writes
-     attr_reader :schema_cache, :visitor, :owner, :logger, :prepared_statements, :lock
+     attr_accessor :pool
+     attr_reader :schema_cache, :visitor, :owner, :logger, :lock, :prepared_statements, :prevent_writes
      alias :in_use? :owner

⚓rake app:updateでActive Storageが更新されないのを修正

# railties/lib/rails/generators/rails/app/app_generator.rb#L309
+     def update_active_storage
+       unless skip_active_storage?
+         rails_command "active_storage:update"
+       end
+     end
+     remove_task :update_active_storage

つっつきボイス:「ちょうどさっき社内でrake app:updateの話をしてたところだった」「重箱の隅ですが、今どきのRailsならrakeじゃなくてrailsじゃないかと😆」「そこ気になってました😆」「まだまだrake使ってるんだよねみんな☺: 自分も使ってるし」「私はrailsでやってます✨」「rakerailsは今はほぼ同じだけど、微妙に違ってる部分もあるのが面倒😓

[Rails 5] rakeタスクがrailsコマンドでもできるようになった

「ところでapp:updateってどのぐらい使われてるんだろうか?」「オレオレRailsアプリでときどき使ってみたことあります: 思ったより書き換えが多くて、特にイニシャライザのコードが多めに書き換えられたのでちょっとビビりました」「Rails wayで書いている限りそんなにおかしなことにはならないとは思うけど、書き換えが広範囲に渡るから差分を見るのが憂鬱になりそうではある😥」「1項目ずつ尋ねられるから、変更内容をその都度吟味すれば大丈夫だと思います」

⚓データベースを変更するコマンドを追加

データベースを変更するrails db:system:changeコマンドを追加

bin/rails db:system:change --to=postgresql
   force  config/database.yml
    gsub  Gemfile

このコマンドは現行アプリの対象データベースアダプタをconfig/database.ymlテンプレートのコピーで置き換え、現行のデータベース用のgemを対象データベース用のgemで置き換える。

Gannon McGibbon
同コミットより大意


つっつきボイス:「rails db:system:changeというコマンドが追加されて、--to=postgresqlみたいにデータベースを切り替えられるようになったそうです」「ほんとだ--to=postgresql…ってそんなことやらねーし🤣」「誰得🤣」「まあ開発のごく初期段階とかならあるのかもしれないけど」

「こういうデータベース切り替えがすんなりいくとは思えないし、無理にRails本体で頑張らなくてもgemの形で導入すればいいんじゃね?って思うし」「ですね」「こういう機能がなまじ公式にサポートされていると、『Railsはデータベースを簡単に切り替えられるよ❤』と勘違いされて乱用されたりしそうだし」「そっちの方が副作用大きそうですね」「新しい機能というだけで使いたがる人もいたりするし、その意味でもgemでやる方がよさそうに思えるけどなー🧐

⚓Rails

⚓Railsは2019年も「あり」か(Hacklinesより)


つっつきボイス:「英語タイトルは煽る気満々ですが」「何つう😆」「辞書的にはdon’t give a fuckは基本的に否定形で使われる俗な言い回しで『どうでもいい』『知ったこっちゃない』みたいな意味ですね」

「Railsは死んだとか終わったみたいな話はもう今に始まったことじゃないし😆」「Rails終わった説もまったく根拠がないわけではないとか、いろいろデータを引用しながらそのあたりを冷静に分析してる感じでした」「やっぱり何も指標がないよりはあった方がいいですし☺

「記事では、Railsの強みは短期開発、ビジネスロジックの実装しやすさ、豊富なライブラリで、向いているのは普通のWebとかEコマース、CMS、コンセプトが流動的なアプリ、プロトタイピングというあたりだそうです」「一度はメジャーに近づいたところもRailsのいい点かも🥰」「Railsに弱みがあるとすればパフォーマンスやスケーリングや柔軟性、用途によってはオーバーキルとか、Rubyが機械学習方面にまだ明るくないとか、だそうです」「自分も誰かに『Railsってどうなんですか?』って聞かれたらだいたいこういう感じで答えるかなー☺

「記事ではRubyの代替としてとりあえずSinatraとHanamiとServerless Rubyが挙げられてますね」「個人的には、SinatraやHanamiだとあまり変わり映えしないし、RailsからSinatraやHanamiに乗り換えるぐらいならRailsのままでいいかなと思うし」「記事でも、用途を選べばRailsは十分『あり』という結論ですね」

「Railsから乗り換えるとしたらServerless Rubyの方が可能性が開けていそうな気がする: Rackのない環境にはそれなりに惹かれるものがあるし、Serverless RubyはRailsでは代替できないので」「ActiveRecordはServerless Rubyで欲しいですか?」「でかいライブラリはサーバーレスだと使いづらいので、ActiveRecordはなくてもいいかな😆

「思ったよりずっとまともな記事だった👍」「タイトルの煽り以外は😃」「これ翻訳したらPV取れそう😋」「翻訳しよっかな😋

⚓Redditに落ちてたテストコードを改善してみた(Hacklinesより)


つっつきボイス:「誰かさんがRedditで『こんなテスト書いたんだけど合ってる?』って質問してて、それに答えた記事だそうです」「『オレならこう書くぜ!』みたいな☺

⚓RSpecが推す書き方とは

# 同記事より(以下同文)
it "must be valid" do
  value(user).must_be :valid?
end

「まず上のコード例↑があって、記事の人はこれで悪くはないけど自分ならcontext使ってこう書く↓と言ってる」

context "fresh instance" do
  it "is valid" do
    expect(user).to be_valid
  end
end

「そしてsubject使うとさらにいい感じになるとも言ってる↓」

subject { FactoryBot.build(:user) }

context "fresh instance" do
  it { should be_valid }
end

「こうやってsubjectを使って書く↑やり方は、(好みを別にすれば)とてもRSpecらしい書き方ですね: RSpecの典型的な見本とも言える」「というと?」「ポイントは、it {}の中にshould be_validだけを書いていること」「あー、つまりRSpecはit {}の中にそれだけ書けば済むことを目指してるんですね?」「まあそういうことです☺

「RSpec的には、it {}の中を極力シンプルにしたい、そのためにsubject { FactoryBot.build(:user) }を事前に書いて下準備している」「subjectってそういう意図だったんだ…RSpecの心をわかってなかった💔

「心情的にはこちらの記事↓と同じく『実はminitestでいいんじゃね?派』ですが😆」「気持ちわかりみです😆」「まあRSpecはDRYに書きたくなるところがありますが」

RSpecえかきうた

「Email uniquenessのこの書き換え↓もいいですね〜: と言いつつ書き換え前のでも全然いいんじゃね?とも思うけど😆」「書き換え前のはベタだけど、やってることは自分にもどうやらわかります」「つかテストはやってることがわかることが肝心だし: テストのリファクタリングに時間かけるぐらいなら😆

context "with an duplicated email" do
  it "is not valid" do
    user.save
    user2 = FactoryBot.build :user
    user2.wont_be :valid?
    user2.errors.count.must_equal 1
    user2.errors[:email].must_equal ["has already been taken"]
  end
end

↓

context "when email is not unique" do
  let!(:other_user) { FactoryBot.create(:user, email: subject.email) }

  it "is not valid" do
    expect(subject.errors[:email]).to eq(["has already been taken"])
  end
end

「なかなかいい記事👍」「RSpec強者からのお言葉でした👑

⚓データベース制約追加時には「multiple migration」戦略を使おう(Hacklinesより)

TechRachoの翻訳記事↓でもよくお世話になってるAndy Crollさんの記事です。

Rails: モデルの外では名前付きスコープだけを使おう(翻訳)

# 同記事より
class AddComposerToSongs < ActiveRecord::Migration[5.2]
  def change
    add_column :songs, :composer, :string, default: "Lin-Manuel Miranda", null: false
  end
end

つっつきボイス:「ほむほむ、これはマイグレーションでのデータベースがロックされる時間をなるべく短くしようという話みたい」「というと?」

「上のコード例みたいに、マイグレーションでadd_columnする段階でdefault: "Lin-Manuel Miranda"みたいにデフォルト値も設定すると、たとえばそのテーブルに1万行あればそのカラムに1万個のデフォルト値が書き込まれるので、それが完了するまでデータベースがロックされてしまいます」「あー🤭」「つまりマイグレーションがそれだけ重くなる🏋🏻‍♀️

「修正後のマイグレーションでは、まずadd_column :songs, :composer, :stringだけを実行する: これならカラムの値はnullになるので一瞬で完了する」「ふむふむ」

「それが終わってから、set_default_composerみたいなデフォルト値設定用のフック↓を一時的に作っておいて、rakeタスクとかでSong.where(composer: nil).update_all(composer: "Lin-Manuel Miranda")を手動で実行することでカラムに値を入れる」

# 同記事より
class Song < ApplicationRecord
  before_save :set_default_composer

  def set_default_composer
    self.composer = "Lin-Manuel Miranda" if composer.nil?
  end
end

「カラムのアップデート後が終わった後に、change_columnで制約をつければ書き込みが発生しないのですぐ終わる」「あとはフックを消せば完了🏁」「なるほどー😃

「長時間ロックされそうなマイグレーションは、こうやって分割すれば原理的にはロック時間を短縮できる、はず」「実際にこのとおりにやるかどうかは別にしても、ものすごく巨大なテーブルを扱うときにはこうした点を常に意識する必要があります🧐」「でないとサービスが長時間停止してしまいかねない…😇

⚓rails_async_migrations: マイグレーションで非同期をサポート

# 同リポジトリより
class Test < ActiveRecord::Migration[5.2]
  turn_async

  def change
    # data update logic you would put into a worker here
  end
end

つっつきボイス:「たまたまマイグレーションねたが続きました」「確かにマイグレーションは時間がかかるから、非同期に実行できればそれに越したことはないけど🤔」「この図↓の右は普通のマイグレーションでしょうか?」「左はsidekiqで動いていて右は通常のマイグレーションということでしょうね」


同リポジトリより

「うーん、やりたいことはわかるけど、ニーズがどのぐらいあるのかよくわかんない😆」「★もそんなにないですね」「こういうgemが必要になるようなユースケースがまったくないわけではなさそうだけど、よほどバカでかいテーブル使ってるfatなRailsアプリとか?」「無理に一般化するより個別にやる方がいいのかな🤔」「このgemがあってうれしいという状況が思いつかないなー: あって逆につらいとかはありそうだけど😆

⚓Action TextがRails 5.2でも動いたお(Ruby Weeklyより)


つっつきボイス:「Action TextがRails 5.2でも動いたそうです」「gemになってるからやれそうではあるけど、Action Textの元になった例のエディタ使えば…って何って名前だっけ?」「やべ、ど忘れした」「Trix!」

全然関係ありませんが、TRIXというフュージョンバンドのコピーを数年前手伝ったことがあります。そっち方面に疎いので曲名も思い出せません😅

⚓スクラムガイドから削除された5項目(Hacklinesより)


scrum.orgより


つっつきボイス:「スクラムのガイドが毎年更新されてるって初めて知りました」「スクラムは団体がちゃんと仕様を策定していますし、いわゆる認定スクラムマスターの資格もPMP並にしっかり整備されていて認定基準も明確なので、勝手にスクラムマスターとか名乗れないようになってますね」「おー!」「アジャイルという言葉は人によって捉え方がまちまちですが、そういうわけでスクラムという言葉はとてもしっかり定義されていて、ちゃんとやるととても厳しい: だから自分はスクラム不得意〜🤣」「🤣

参考: スクラム (ソフトウェア開発) - Wikipedia
参考: 認定スクラムマスター研修とは? - Qiita
参考: PMP - Wikipedia

⚓今度は「Active Deployment」が欲しい(Ruby Weeklyより)


つっつきボイス:「お、ちょうど先週のウォッチで『CapistranoがRailsに組み込まれてもいいんじゃね?』っと話しましたね」「そうでした!」「記事でもまさにCapistranoの話してるし: Railsに標準のデプロイ方法があればいいのにという趣旨、自分もわかりみ😍

「確かに何らかのデプロイ層を標準で定めるというのはありかも?: 今のRailsだと、デプロイ方法が死ぬほどあるとまではいかなくても3種類ぐらいはある感じなので」「うんうん」「コードのみをデプロイする方式とか(sshでもいいしsshじゃなくてもいいけど)、Dockerみたいにできあがったコンテナをガバっと置き換える方式とか、Herokuのようにマネージドなデプロイシステムに乗る方式とか、そのあたりかな🤔」「なるほど!」「HerokuはどちらかというとDockerに近いかも」

「Railsがメジャーになるに連れて、インフラができないアプリエンジニアも増えてきましたし😆」「😆」「それまである程度Web開発の経験があった人ならデプロイもある程度勘が働くと思いますが、インフラを知らないエンジニアだけでアプリを作ってしまうと、二度と同じアプリを作れなくなってしまったりすることもありますし😅

「てなことを考えると、Active Deploymentみたいなのはあってもよさそうだし、つかCapistranoをまるっと取り込んじゃえばいいのに😆」「私も腑に落ちます」

⚓その他Rails

つっつきボイス:「Railsの公式APIドキュメントサイトのhttps://api.rubyonrails.org/で、ディレクトリ名を/v4.2.11/みたいに指定すると、過去バージョンのAPIドキュメントを表示できることに気づきました: 近々簡単な記事を公開します」「なるほど、でも自分はやっぱり複数バージョンを串刺し検索できるhttps://apidock.com/rails)↓が使いたいんだよ〜😭: どうしてRails 4.2.7で更新停止しちゃったんだろう」「やっぱりそうですよね😅


apidock.comより


⚓Ruby

⚓JetsとRubyでAWS Rekognition動かすサーバーレスなSlackコマンド作った(RubyFlowより)


同記事より

# 同記事より
class ScrapeImagesController < ApplicationController

  def scrape
    render json: { # Slackに即座に応答する
      response_type: 'ephemeral',
      text: "Slackコマンドのデプロイ完了!"
    }
  end

end

つっつきボイス:「今が旬のサーバーレス🥕: 以前のウォッチでも取り上げたJets↓を使い始める人が出てきたのか」「ですね😋」「JetsならAWSで手軽にさわれるし、記事もまさにそういう感じだし✈

「AWS Rekognitionって認識サービスか何かでしょうか?」「知らんかったけど、どうやらそうみたい↓」

「ちょっと使ってみたい気がする😍: ちょうど今日のチーム内発表でもSlackとの連携にGAS(Google Apps Script)↓を使ってましたが、自分ならJetsを試したかも」「😍」「JetsはAWSにデプロイするところまでカバーしてたと思うし、筋は悪くなさそうだし」

「そういえば私も2年前ぐらいにGASとかGCP↓で遊んでみたことがあったんですが、当時はまだツールがPython2だったりと少し混乱気味だった印象でした: 最近やっとPython3になってだいぶ整理されたようです😋」「たしかGoogleにはPythonの偉い人がいるから中々上げられなかったのかも?」

参考: GCP と G Suite を含む Google Cloud - 無料トライアル  |  Google Cloud

「AWSは最初がちょっとめんどくさいんですよね😆」「GCPも違う意味で最初面倒だった印象😆」「でもGCPはボタンひとつでプロジェクトを作ったりできるじゃないですか: AWSは小さなサービスをいろいろ組み合わせて作らないといけないので、Lambdaを書いただけでは動くようにならないという😅」「あー」「API Gateway↓とつないだりとか、いろいろしないといけない🧐

参考: Amazon API Gateway(API を簡単に作成・管理) | AWS

「GASって確かGCPよりも前からあったんですよね: だもんで元々お手軽ではある😋」「私がGCPで遊んでたのはちょうどそれらが統合されようとしている最中だったので、サービス名が変わったりドキュメントが追いついてなかったりとかがつらかった覚えが😅

追記(2019/01/24)

情報ありがとうございます!🙇

⚓Ruby 2.6.0p0のNet::Protocolのセキュリティ問題 — 修正済み(Ruby Weeklyより)

# 同記事より
PAYLOAD_SIZE = 8_301_500
data = 'a' * PAYLOAD_SIZE + '···'

client = Api.new
client.get
client.add(data)
client.del(data)
puts client.get #=> ["aaaaaaaaaaaa...\xC2\xB7\xC2\xB7\xC2\xB7"]

つっつきボイス:「ざっとしか見てないけど、uncompleted payloadの周りの処理に問題があるらしき: 指摘さえしてもらえればRuby側で対応できそうな問題かな」「お、既に修正コミット↓が出てると最後に書いてありますね」「パッチモンスターことnobuさんがやってくれた😍」「Ruby 2.6.1で反映されるんですね👏

参考: Fix Net::Protocol::BufferedIO#write when sending large multi-byte string · ruby/ruby@1680a13

⚓pipe_operator: RubyでUnixのパイプ操作を実証してみた(Ruby Weeklyより)

# 同リポジトリより
"https://api.github.com/repos/ruby/ruby".pipe do
  URI.parse
  Net::HTTP.get
  JSON.parse.fetch("stargazers_count")
  yield_self { |n| "Ruby has #{n} stars" }
  Kernel.puts
end
#=> Ruby has 15120 stars
-9.pipe { abs; Math.sqrt; to_i } #=> 3

# Method chaining is supported:
-9.pipe { abs; Math.sqrt.to_i.to_s } #=> "3"
sqrt = Math.pipe.sqrt #=> #<PipeOperator::Closure:0x00007fc1172ed558@pipe_operator/closure.rb:18>
sqrt.call(9)          #=> 3.0
sqrt.call(64)         #=> 8.0

[9, 64].map(&Math.pipe.sqrt)           #=> [3.0, 8.0]
[9, 64].map(&Math.pipe.sqrt.to_i.to_s) #=> ["3", "8"]

つっつきボイス:「ElixirやUnix風のパイプをRubyで実証したかったようです🚬」「関数型勢が好きそう☺: しかし遅延評価とかやってそうな雰囲気だし、ぱっと見UnixというよりはElixir的に見えるけどな〜: Elixir知らないけど😆」「やっぱそう思いますよね? Unix風というからには|でしょうけど、Rubyでその記号でつなぐわけにはいかなさそうだし😆」「PROOF OF CONCEPTと書いてあるから、実用というよりはアイデアの実証なんでしょうね☺

参考: パイプライン演算子 · Elixir School

Elixirでは|>だそうです。

⚓solargraph: RubyのLanguage Server(Ruby Weeklyより)



solargraph.orgより


つっつきボイス:「お、RubyのLanguage Serverじゃないですか🥰」「このSolargraphって初めて見ましたが新しい割に★500超えてますね」「数年前にRubyKaigi↓でLanguage Serverが盛り上がってた気が」「そうでした!あのときのとはまた別物みたいですね」

参考: Ruby Language Server - RubyKaigi 2017

「やっぱりLanguage Serverがあるといい😋」「エディタでLintをかけるときとかに使うヤツですね😋

参考: lint - Wikipedia

「JetBrainsはLanguage Serverを喜びそう」「あ、JetBrainsは確かUpsourceというチーム系プロダクトでそういうの持ってますよ🧐」「え、知らなかった😳」「Upsourceならコードジャンプも含めてほぼIDEと同じことできるので強力😎」「へー!」「なのでおそらく実質Language Serverと同じものを持ってるはず: LSPとかでAPI公開まではしてなかったと思いますが」

参考: Upsource: Code Review, Project Analytics, and Team Collaboration by JetBrains

参考: IDE上でコードレビュー完結!Upsourceを使ってみる - Qiita

「みんなLanguage Serverでやれれば理想ですね」「エディタごとに独自対応する苦労がなくなるのはいいこと😙

ついでにLanguage Serverの情報がまとまってるサイトも見つけました↓。


langserver.orgより

⚓GemfileなしでBundlerが使える話(Ruby Weeklyより)

短い記事なので引用はしません。


つっつきボイス:「Gemfileなしでもgemfile do endブロックでBundlerが使えるらしいです」「あれ?こういうのちょっと前にも見たことがある気がするナ🤔」「そういえば既視感ある…ウォッチで扱ったかな🤔

ありました↓。「Rails OOP上級編」です。

週刊Railsウォッチ(20180918)ビューテンプレート探索が高速化、mini_scheduler gem、レガシコントローラのリファクタリングほか

⚓Bundler 2の続報


bundler.ioより


つっつきボイス:「例のBundler 2に続報が出てました」「お、Bundler 2にはv1とv2を自動的に切り替える機能があるけど、それでつまずく可能性があるのか😨: でもGemfile.lockファイルのBUNDLED WITHで指定されているバージョンを指定してbundlerをインストールすれば回避できると」「知らないとハマりそうですね😭」「たしかすごく古いBundler使ってた頃はBUNDLED WITHがなかったと思う」

Can't find gem bundler (>= 0.a) with executable bundle (Gem::GemNotFoundException)

⚓その他Ruby


つっつきボイス:(○×※●…)「これはやさしすぎ😆」「やっぱり😆



つっつきボイス:「上は何と比べて遅いんだろか?🤔」「どれどれ…あ、PHPerKaigi↓か」

「PHPの人たちが自分たちでペチパーって呼んでるとは知らなかったー」「PHPerKaigiはいかにも日本国内っぽいので、海外事情を考えなくていい分締切は遅いんでしょうね☺: 国際的なカンファレンスの締切は早いのが普通」

⚓Ruby trunkより

⚓提案: ブロックなしのProc.newでwarningを表示しないで欲しい場合がある

# 同issueより
def foo bar = Proc.new
  bar.call
end

foo      { p "block" } # warn
foo ->() { p "block" } # no warn

つっつきボイス:「tenderloveさんからのissueで、この書き方がRailsでそこそこ使われているらしいのでwarningを出さないで欲しいそうです」「こんなwarningを出す機構がRubyにあったとはー😳」「->()だと出ないんですね」

「ついでにko1さんが、こんな変な書き方↓を見つけたと書き込んでました」「うひょー、ここでlocal_variable_getするか普通😆、キモい、キモすぎる🥶」「う、わかんない😅」「引数の:bはこの瞬間にどう評価されるんだろうか…謎すぎる」「実装に依存するって書いてますね」「これはそうでしょうね: JRubyとかでどう変わるかわかったもんじゃないし」

# 同issueより
def foo o = binding.local_variable_get(:b), &b
  p o.class
end

foo{} #=> Proc

参考: instance method Binding#local_variable_get (Ruby 2.6.0)

引数 symbol で指定した名前のローカル変数に設定された値を返します。
docs.ruby-lang.orgより

⚓クラウド/コンテナ/インフラ/Linux/Serverless

⚓AWS Lambdaでカオスを注入(Serverless Statusより)

コードはPythonですが。


つっつきボイス:「記事にあるLambda Layerって何でしょうか?」「Lambda Layerは最近のAWSに入ってきた機能のひとつで、たとえばDockerのOverlayFSみたいに、環境Aと環境Bと環境Cのレイヤをかぶせてひとつの環境にするみたいなことができるものですね」「なるほど!」

参考: 新機能 – AWS Lambda :あらゆるプログラム言語への対応と一般的なコンポーネントの共有 | Amazon Web Services ブログ
参考: Overlayfs - ArchWiki

「SSMというのも初めて見ました😅」「SSMはたしか、Lambda系の機能を簡単に使えるようにするもので、SSMパラメータストアからLambdaで環境的な値を取ってこれるみたいなヤツ」「AWS System Managerの略語がなぜかSSMなんですね…」

参考: AWS Lambda functionと EC2 Run Commandで、かんたんSlash Commandデプロイ - Qiita
参考: LambdaでAWS Systems Manager パラメータストア(KMS暗号化)を利用する際に注意すべき性能について | Skyarch Broadcasting

「で、記事はLambda Layerを使ってカオスライブラリを注入する方法を丁寧に説明していますね: なかなかいい記事👍」「カオスって前に教えていただいたカオスエンジニアリング↓的なものでしょうか?」「ちゃんと読んでませんが、上の記事のはNetflixがやってるようなカオスエンジニアリングとはまた違うっぽい: Lambdaのレイテンシをランダムに変えるレイヤライブラリ程度のものかなと」「あ、そうでしたか💦: ちらっとchaos engineeringという言葉が見えたんですが、よく見たら以下の記事にインスパイヤされたという記述でした😅

参考: Netflix 驚異的なトラブル対応 カオスエンジニアリングとは、何か?

参考: Applying principles of chaos engineering to AWS Lambda with latency injection

⚓GCP Cloud FunctionsでGo 1.11がサポート


つっつきボイス:「Go言語ってもう1.11なんだ」「ですね: 目玉はGoでWebAssemblyバイナリを吐けるようになったことですが、まだどう使おうかみんな迷ってる雰囲気です」

mrubyをWebAssemblyで動かす(翻訳)

「GCPのCloud Functionsが今までNode.jsとPythonだけだったのが意外でした」「そうそう、GCPのはまだ対応言語が少なかった: AWS Lambdaの方はこの間対応言語が一気に増えましたし🏋🏻‍♀️」「Cloud Functionsも尻に火がついた感じですね😆: ちょっと前の記事ですが、そのあたりの現状がまとまってました↓」「記事ではLambdaのPHPとか未対応になってたけど、今じゃPHPもSwiftも含めてLambdaで動かない言語はほぼないし😆: DockerはさすがにLambdaじゃ動かせないかなー」

参考: 各Cloud Functions で使える言語比較(2018年7月) - Qiita

「お、よく見るとIBMのCloud Functionsも強い💪」「ほんとだ、去年半ばの時点でたいてい網羅してるし😳」「IBMはSwiftのコンパイラも持ってるってことか: さすがIBMパワー🔵



ibm.comより

⚓その他クラウド


つっつきボイス:「これねー、ちょっとよくない流れかなー😢」「ですね、しかもあのRedisがやってるというのが😢」「Redisはしょっちゅう使ってるから下手すると巻き込まれそう: MongoDBとかは個人的にどうでもいいけど😆」「😆」「もしかするとMySQLとMariaDBのときみたいに分裂するかも?」「あー」「商用に使われたくないみたいな気持ちはわかるんだけど、過去にそうやって優秀なソフトウェアが分裂することがちょくちょくあったので💔

「PassengerとかGitLabみたいに、EnterpriseエディションとCommunityエディションを分けるぐらいならいいけど、Redisには名前変わって欲しくないなー」「同意です」「オープンソースを商売として軌道に乗せるのはいろいろ難しい…」

⚓SQL

⚓Dejavu: ElasticsearchのWeb UI(DB Weeklyより)


opensource.appbase.io/dejavuより


つっつきボイス:「もしかしてよくあるやつでしょうか?」「ElasticsearchのWeb UIはまあいっぱいありますが、このDejavuはネイティブなElasticsearchにクエリを投げるっぽいかな: 悪くなさそうに見えるのでちょっと動かしてみたい気はしますね😋」「😃

⚓JavaScript

⚓知っておきたいES2018の新機能(JavaScript Weeklyより)

// 同記事より
const arr1 = [10, 20, 30];

// make a copy of arr1
const copy = [...arr1];

console.log(copy);    // → [10, 20, 30]

const arr2 = [40, 50];

// merge arr2 with arr1
const merge = [...arr1, ...arr2];

console.log(merge);    // → [10, 20, 30, 40, 50]

つっつきボイス:「ES2018は知らないことだらけ」「ドット3つの...↑なんてのがあるのか!ドット2つ..は前からあった気がするけど」

参考: 「…」←これ、ただの省略記号かと思ってました。(Spread operatorのお話)|もっこりJavaScript|ANALOGIC(アナロジック)
参考: JavaScriptで123.toString()はエラーになるけど、123..toString()は正常に動作する。これってトリビアの種になりませんか? (JavaScript おれおれ Advent Calendar 2011 – 12日目) | Ginpen.com

「そういえばこういうenumerable↓なんてのもあったけど、こういうPrototypeの根っこのあたりはできるだけ触らないようにしてたからなー」「怖そう😨」「よく理解しないうちに使って地雷を踏みたくないし💣

// 同記事より
const car = {
  color: 'blue'
};

const car2 = Object.create(car, {
  type: {
    value: 'coupe',
    enumerable: true,
  }
});

console.log(car2.color);                      // → blue
console.log(car2.hasOwnProperty('color'));    // → false

console.log(car2.type);                       // → coupe
console.log(car2.hasOwnProperty('type'));     // → true

console.log({...car2});                       // → {type: "coupe"}

「Asynchronous Iterationなんてのも😳」「なるほど、こう動くと↓」「このarr[Symbol.iterator]()って記法は何だろう…」「Symbol.iterator自体はES2015からあったのか」「おー」「function * ()とか何が起こるのかもわからん😆」「他のプログラミング言語でできてたことができるようになったんだろうなとは思うけど、見慣れない書式はどうもねー😆

// 同記事より
const arr = [10, 20, 30];
const iterator = arr[Symbol.iterator]();

console.log(iterator.next());    // → {value: 10, done: false}
console.log(iterator.next());    // → {value: 20, done: false}
console.log(iterator.next());    // → {value: 30, done: false}
console.log(iterator.next());    // → {value: undefined, done: true}

「やや、Promiseにとうとうfinallyが入るし!」「おースゲー😻」「ってことは今までなかったのか😆」「えぇー、そうだったっけ😅

// 同記事
fetch('https://www.google.com')
  .then((response) => {
    console.log(response.status);
  })
  .catch((error) => { 
    console.log(error);
  })
  .finally(() => { 
    document.querySelector('#spinner').style.display = 'none';
  });

⚓ES2018の正規表現

「おー、ES2018で正規表現も拡張されるし」「何と!正規表現厨としては身を乗り出しちゃいます🥰」「名前付きキャプチャみたいに基本としか思えない機能がやっと入るのね」「何という今さら😆」「後読み(Lookbehind)もやっと人並みになったー😂」「正規表現でUnicodeプロパティも扱えるようになるし」「そうっ!! それもずうっと待ってました😤

// 同記事より
const re = /\b(?<dup>\w+)\s+\k<dup>\b/;
const match = re.exec('Get that that cat off the table!');        

console.log(match.index);    // → 4
console.log(match[0]);       // → that that

[連載:正規表現] Unicode文字プロパティについて(1)

「いやー今までJSの正規表現はどうも不自由だなって思ってたけどやっぱり正しかった😆」「😆」「6年ぐらい前にいろんな言語の正規表現を調べたんですが、そのときにJSの正規表現は機能が足りなすぎてアカンと思ってずっと避けてました」「まあJSで正規表現バリバリ使う感じじゃなさそうだけど」「何しろJSにはLookbehind自体がなかったし、他の言語と違ってサードパーティのネイティブ正規表現ライブラリも導入できないし、悲しすぎました😭

JavaScript: Chrome V8なら正規表現で後読み(look behind)がフル機能で使える

「でもこれでJSの正規表現がとうとう『ボクらの知ってる正規表現』になってくれる感🎊」「RubyやPerlやPythonではとっくにできてた機能😆」「長かった、長かったよー😂

「ということは、正規表現の分JSがデカくなるだろうから、ES2018は組み込みだと苦しくなるかもしれない🤔」「あーありそう」「Unicodeの文字プロパティとかいかにもデカくなりそうだし」

⚓CSS/HTML/フロントエンド/テスト

⚓「次世代 Web カンファレンス 2019」終了


つっつきボイス:「昨年のウォッチで軽く紹介した、法政大学で行われたWebカンファレンスです」「ああ例の、初心者お断り感が強く漂う、強者による強者のためのWebカンファレンスですね☺」「勝てる気がしない😆」「ひたすら議論だけしたいみたいなスタンスだったかな」「記事の最後に『次回とか全く考えてない』とありました」「たしか知人も参加してたはず…いたいた🎯」「こういう議論をできる場というのはたしかに欲しくなりますね」

⚓Yet Another REST Client


つっつきボイス:「YARCって、もしやとっくに有名なんでしょうか?」「自分はこのRESTクライアント入れてます↓☺」「Restlet!、こういうのもあるんですね」「昔また別のクライアント入れてたことあったけど、アップグレードしたら動かなくなった😭

「ちなみにこういうRESTクライアントは、プロファイルを作ってチームで共有するとデバッグやテストがとってもやりやすくなる😎」「そうそう、そうなんですよ~😂

「うろ覚えなんですが、このRestletをうまく設定するとライブラリからも読み込めるようになって、GUIで作ったリクエストをテストコードで読み込んでそこからリクエストを投げるなんてことも確かできた」「おぉ〜!」「そうしておけば、テスターの人が自分で組み立てたリクエストをそのままテストコードに読み込ませてテストできるので、人間が行ったテストを繰り返し実行するなんてことが比較的やりやすくなる🥰」「いいこと聞いた😋」「でも所詮リクエスト単位になっちゃうので、シナリオ的な動作には向いてませんが😆

⚓その他フロントエンド

⚓言語

⚓「低レイヤを知りたい人のためのCコンパイラ作成入門」

⚓その他

⚓Git Explorer: 「gitのあのコマンド何だっけ」を支援


同サイトより


つっつきボイス:「Gitのあのコマンドって何だっけ?を探せるサイトだそうです」「自分だったらRubyMineで検索するかな🤣」「ワイも〜🤣」「RubyMineはいろいろ強いし💪

⚓住所.jpの住所データベース


同サイトより


つっつきボイス:「このjusyo.jpのデータベースはたまたま見つけたんですが、日本郵便のブチブチにちぎれた郵便番号CSVと違ってちゃんとwell-formedになってて、しかもメンテナンスが継続してるのが、とてもとてもうれしかったので😂」「SQLite3とかのファイルをそのまま置いてくれてるなんて、エラ〜イ👍」「そうそう、日本郵便のKEN_ALL.csvはいろいろ地獄感あってヤバい👿

「KEN_ALL.csv、最近だと知らない人も多いのかな👴」「昔はよくSQLを初めて学ぶときなんかにサンプルとして使ったりしてましたね」「そしてKEN_ALL.csvはまともにパースできない」「そう!😤、いかにも昔のホストコンピュータっぽい固定フィールド長でした」「はりゃ〜公式データなのにひどい😅」「でも日本の郵便番号システムは、そもそも1つの郵便番号に複数の町村名が割り当てられたりする↓から完全になりようがないし」「そうなんですよ: ひどいよジャイアン😭

参考: KEN_ALL.CSV (郵便番号検索)の落とし穴 - Qiita

# 同記事より
498-0000 三重県桑名郡木曽岬町, 愛知県弥富市
618-0000 大阪府三島郡島本町, 京都府乙訓郡大山崎町
871-0000 大分県中津市, 福岡県築上郡吉富町

「なのに地名から郵便番号を検索しようとすると今度は情報が足りないという😇」「どうにもならない😇」「jusyo.jpもさすがに日本の住所を隅から隅まで完璧に網羅しているわけではないようです」

「jusyo.jpのSQL化されたデータの何がありがたいって、京都市内の恐ろしく難解な道案内住所表記をちゃんと別フィールドに分けているところ😂😂

参考: 難解な京都の住所表記。3パターンを抑えればとても分かりやすいです! | テナントプラス営業ブログ

「jusyo.jpはデータも継続的にメンテされてるみたいでそこもうれしいです😂」「でないと意味ありませんしね☺

「そういえば銀行コードのデータベース↓も更新割と多いんですよね」「あー!そうかも」「支店が統廃合されたりしたらデータベースに速攻反映しないと『お金おろせないんですけど』とかたちまちクレームが集中するし😇」「実はすっごく大事なデータベース」

参考: 金融機関コード・銀行コード検索

⚓社内自作キーボード愛好家に

⚓その他のその他



つっつきボイス:「これ見た見た😆」「昭和100年のインパクトが😆」「今は昭和94年なのか」「きっと2000年問題の対応が間に合わなかったとかで、内部データをむしろ昭和に切り替えて、表示だけ平成にして切り抜けちゃえみたいな対応だったんだろうなとか😇」「いかにも〜😇」「いろいろしどい😇

参考: 2000年問題 - Wikipedia

⚓番外

⚓宿題を手伝うAlexa

逆説的ですが、小学校低学年までなら反復練習を促進する意味でむしろありかもと思ってしまいました。

参考: 「アレクサ、5ひく3は?」Amazon Echoが6歳児の宿題を手伝う | ロボスタ


つっつきボイス:「むしろ子どもの学習意欲が高まるんじゃないかと思って」「耳で覚えるとか、暗記系には結構いいかも?」「英語なんかもそうだけど、しゃべった方が覚えられるというのはあるし」「Alexaなら当分文章題なんか解けないだろうし、目くじら立てるよりそのぐらいに考えて平和利用する方がいいかなって」


今回は以上です。

おたより発掘

バックナンバー(2019年度第1四半期)

週刊Railsウォッチ(20190115)Rubyの<=でクラス同士を比較、Rubyの記号の読み方いろいろ、Ruby C API解説サイトほか

今週の主なニュースソース

ソースの表記されていない項目は独自ルート(TwitterやRSSなど)です。

Rails公式ニュース

Ruby Weekly

RubyFlow

160928_1638_XvIP4h

Hacklines

Hacklines

Publickey

publickey_banner_captured

DB Weekly

db_weekly_banner

Serverless Status

serverless_status_banner

JavaScript Weekly

javascriptweekly_logo_captured

Rails: api.rubyonrails.orgで過去バージョンのAPIドキュメントを参照できるURLリスト

$
0
0

小ネタで恐縮です。
RailsのAPI検索といえば、複数バージョンを串刺し検索できる以下のAPIdock.comが長らく定番でしたが、惜しくも4.2.7で更新が停止したままです😢


apidock.comより

api.rubyonrails.orgは名前からして公式のRails APIドキュメントサイトで、Railsの更新も即座に反映されているようですが、惜しくも最新版しか表示できません。


api.rubyonrails.orgより

と、さっきまで思っていました。

しかしたまたま、以下のURLで過去のAPIドキュメントページを表示できることがわかりました。

api.rubyonrails.orgの過去バージョンURL

以下のリストのバージョンのパッチ番号は、それぞれ各メジャー/マイナーバージョンの最終パッチ番号です。

たとえばURLの/v4.1.16//v4.1.15/に変更すると、そのパッチ番号のAPIドキュメントを表示できます(すべてのパッチ番号を確認したわけではありませんが)。

参考: セマンティック バージョニング 2.0.0 | Semantic Versioning

Rails v2.3.11まで遡ると、ドキュメントのスタイルが現在とだいぶ異なっていますね↓。検索窓もありません😓


api.rubyonrails.org/v2.3.11より

なお、該当メジャー/マイナーバージョンがない場合はhttps://edgeapi.rubyonrails.org/にリダイレクトされます。 edgeapiには現在Rails 6のAPIドキュメントが置かれています。

とはいうものの、api.rubyonrails.orgで複数バージョンのAPIドキュメントを串刺し検索できないことに変わりはありません。もうちょっとナビゲーションが親切だったら…。

Railsガイド英語版でも同じ方法が使える

同じ方法が英語版のRailsガイド↓でもできることがわかりました。

冗長なのでリストにはしませんが、https://guides.rubyonrails.org/https://guides.rubyonrails.org/v2.3.11/のようにすることで過去バージョンのRailsガイドを表示できます。

上記APIドキュメントと同じバージョンがすべてあるかどうかまでは確認していません。少なくともv1.xはないようです。

おたより発掘

Railsは2019年も「あり」か?#1(翻訳)

$
0
0

概要

原著者の許諾を得て翻訳・公開いたします。

原文が長いため3分割してあります。
日本語タイトルは原文タイトルではなく内容に即したものにしました。
画像は元記事からの引用です。

Railsは2019年も「あり」か?(翻訳)

Post image

もう認めようではありませんか、Ruby on Railsが年を取ったことを。いや本当に長生きしたものです。しかしtech業界の移り変わりを見渡してみれば、Railsのテクノロジーは驚くほど成熟していると考えられるので、Railsを特定用途に用いることに何の不思議もありません。

今さらこんな話題を蒸し返す意味はあるのでしょうか。RubyのコミュニティとRuby on Railsのコミュニティにとって、ここ数年は企業や開発者の関心が失なわれつつあるというエビデンスによって、厳しい試練にさらされていました。RubyとRailsが今日の標準に照らし合わせて著しいデメリットを多数抱えているという理由で、他のテクノロジーを選ぶスタートアップ企業や開発チームが増えています。

「Rubyは死んだ」「Railsは死んだ」と主張する人もいるでしょうし、その理由には的を射た部分もあるでしょう。決して根も葉もない噂ではありません。

2011年から開発にRubyを用いてビジネスを継続してきた当Rebased社は、さまざまなサーバーサイドWebアプリケーション開発のオルタナティブを評価してきました。その結果、Ruby on Railsは2018年末の時点においても、ほとんどのユースケースにおいて、セキュアかつ高品質のWebアプリを短期間で開発するのに最もふさわしいと私は確信いたします。リッチなJavaScriptアプリの登場によってビジネスロジックの一部がフロントエンドに移行しましたし、バックエンドの責務は「サーバーレス」ツールの勃興によって縮小され、単にSQL->JSON変換と認証をこなすだけのものに成り下がりつつあります。しかし、構築するバックエンドの規模が数個のリモート関数呼び出しより大きくなるのであれば、私たちにとってRuby on Railsは今も変わらず選択肢のひとつに含まれています。ただし「Railsにしておけば万事OK」ということではありません。用途によっては、他のテクノロジーを使う方がずっと適切な場合もあるからです。しかし総合的に見れば、2019年も私たちにとって「ほぼほぼRailsを使う」年となる見通しです。
Tomasz Stachewicz(RebasedのCEO)

かつて、RailsがあのWeb 2.0の申し子扱いされていた時代がありました。それまでPHP開発者たちはスパゲッティコードを長年量産しながら苦しみ続けたというのに、ブログアプリをわずか15分で、しかもまともそうなコードで構築できるようになったのですから、当時は目を見張ったものです。

Rails登場後の数年間は、マーケットプレイスやeコマースサイトからSNSに至るさまざまな製品にうってつけの構築ツールとしてもてはやされました。その状況が一変したのは、スマホの人気上昇とともにアプリへの要求が著しく複雑化したときでした。

Ruby on Railsのテクノロジーは完全無欠でも何でもないことがわかってきました。機械学習やブロックチェーン(2019年はどうなることやら)といった特定の問題を扱うには明らかに不向きです。TwitterはRuby on RailsからScala言語に移行し、他のプラットフォームもGo言語やRust言語を選ぶようになったことは、Ruby on Railsコミュニティにとって痛恨の一撃でした。Ruby on Railsは世界中にいる数百万人のユーザーをさばくには力不足であることに誰もが気づきました。Railsをマルチサーバー上でスケールアップすれば別ですが、それはまた別の話であり、かつ高くつきます。

それから数年経った今、私たちは1つの問いかけを自分たちに投げました。「Ruby on Railsは本当に死んだのか?」「今後もRuby on Railsで自分たちの製品を構築するのはありなのか?」。私たちと一緒に答えを探ることにしましょう。

統計をチェック

StackOverflow

ruby-on-rails-stackoverflow-trends

StackOverflowのデータをちょっと見てみましょう。Rubyコミュニティ、そしてRuby on Railsコミュニティのエンゲージメント率は、Node.jsとは逆に明らかに大きく減少しています。Node.jsのコミュニティは絶え間なく成長し続け、人気も上昇の一途ですが、Ruby on RailsフレームワークとRuby言語は、かつてないほど0%に接近しています。

StackOverflow Developer Survey

ruby-on-rails-stackoverflow-survey-2018

StacOverflowは、特定のプログラミング言語それ自体や、その言語の業界での一般的な位置づけについて開発者にアンケートを取るのが毎年の恒例です。その質問の中に「業務で使うプログラミング言語」という項目があり、結果は上のグラフのとおりです。

同じ調査の中で、Ruby開発者のグローバルなサラリーが64,000ドルという結果が出ています。これはClosureClojure(72,000ドル)やRust(69,000ドル)はおろか、Go言語(64,000ドル)をも下回っていますが、JavaScriptの55,000ドルと比べれば大幅に上回っています。

事実はどうなのか

同じ結果を米国に絞り込んで見ていくと、少々趣が異なっています。

ruby-on-rails-salary-indeed-com

ご覧のとおり、Ruby開発者はこのカテゴリ内で明らかに収入の高い仕事にランクインしていますが、その役職の枠は減少しつつあり、Node.jsの開発者が就ける役職の枠は、Ruby on Rails開発者の2倍近くあります。

Rails開発者の収入はNode.jsよりも上

このデータを元に判断すると、Node.jsをやってきたプログラマーのサラリーは、明らかにRuby on Railsプログラマーのそれを大きく下回っています。この要素は、Web製品を開発するときの総合的な開発コストに大きく影響を与える可能性がありますので、頭に置いておきましょう。

仮に、あなたが製品をスクラッチから開発するためにチームメンバーを招集することになったとします。私たちの経験から申し上げると、スキルの高いプログラマーをこのマーケットで集められるかどうかについては大した問題とならないはずです。胸の躍るような新規プロジェクトを求める腕の良いRails開発者は今も数千人はいますが、今そうした開発者の数は減少しつつあります。もちろん、この辺の事情は地域によって変わってきますので、まずは皆さんの地元の事情を調査することをおすすめします。でなければ、単に開発をアウトソースするかコソーシング(co-sourcing)しましょう。

RedMonkのデータ

Rubyは、2018年6月の時点で6位につけています。

redmonk-ruby

RedMonkは、GitHubやStackOverflowから言語への関心のデータを取り出し、それを他の主要な言語すべてと比較しています。悲観論者の言うこととはうらはらに、Rubyは死んでいるどころかその逆であることがはっきりわかります。Rubyが上位につけていることを考えれば、Rubyは今も開発ツールやアプリで用いられる言語として人気を保っていると結論付けられます。でも「喜ぶのはまだ早い」のです。

GitHub Statsのデータ

github-osctoverse-ruby-on-rails

上のグラフは、GitHubコントリビュータたちの間で人気の高い言語を比較しています。具体的には、あるテクノロジーを用いて開発されたリポジトリの数を比較しています。不動の勝利を収めている言語についてはだいたい見てのとおりです。グラフからわかるように、Rubyを選ぶプロジェクトは減少の一途を辿っており、今も人気トップ10にランクインしているものの、利用数は年を追うごとに減っています。

Ruby Gem

この流れで、今度はGemのダウンロード数をチェックしてみましょう。InfinumはGemのダウンロード数についてまとめた良記事を公開しています(訳注: 記事では2017年を対象としています)。Infinumはダウンロード数を頻繁にトラッキングして自分たちのデータベースに保存しています。この結果からRuby on Railsの現状についてさらに情報を得ることができます。結果はあまり芳しくありません。

Infinumが集めたデータによれば、新しく作成されるGemの数は年々減っており、リリース数も同じく減少しています。一方、Ruby on Railsのダウンロード数は前年度に比べて非常に増えているようです。

2019年: Rubyにとって2018年はかつてないほどよい状態でしたが、2019年にはさらに大きな可能性を秘めています。Rubyは、自らが支配するWeb開発やバックエンドAPIのニッチな分野で急速に成長しています。私は2004年からRubyのマーケットをウォッチしていますが、今ほど胸が高まったことはありません。私は、他の言語からRubyに移った開発者を何人も目にしています(我がArkencyだけでもC#言語出身のプログラマーが2人います)。Rubyは開発者を今も幸せにし続けており、同時にエコシステムの成熟も大きく進んでいます。私がRubyコミュニティで特に興奮を覚えるのは、DDD(ドメイン駆動設計)CQRS(コマンドクエリ責任分離)イベントソーシングの技法やミューテーション解析の採用が増加していることです。
Andrzej Krzywda(Arkency


関連記事

Railsは2018年も現役か?: 前編(翻訳)

Railsドメイン設計: イベントソーシングでCQRS Read Modelが基本的に必要な理由(翻訳)


週刊Railsウォッチ(20190128)Rails 6のオートローダーがZeitwerkに置き換わる?Rails 6はRuby 2.5が必須、最近のSQLiteほか

$
0
0

こんにちは、hachi8833です。LinuCLPI-JapanLinux標準教科書は無料のPDF版よりKindle版(200円)の方がKindleでは見やすいことに気づきました。ついでに英語版↓も制作されていたことを知りました。


つっつきボイス:「Linux標準教科書って何か英語版を元にしてるとかじゃなくって?🤔」「かなと思ったんですが、『JICAボランティアのご協力によりLinux標準教科書が英語化されました』とあるので日本語から訳したみたいです」「ま英語圏にはいい教科書がいっぱいあるし😆」「😆」「日本人が英語の書籍を読まなさすぎというか📚

  • 各記事冒頭には⚓でパーマリンクを置いてあります: 社内やTwitterでの議論などにどうぞ
  • 「つっつきボイス」はRailsウォッチ公開前ドラフトを社内有志でつっついたときの会話の再構成です👄
  • 毎月第一木曜日に「公開つっつき会」を開催しています: お気軽にご応募ください

⚓週刊Railsウォッチ「公開つっつき会#7」開催のお知らせ

次回の公開つっつき会は2/7(木)に開催いたします。皆さまのご参加をお待ちしております🙇

⚓Rails: 先週の改修(Rails公式ニュースより)

先週のウォッチまでに公式の更新情報をだいぶ先回りしたので、コミットログからも少し見繕いました。

⚓Rails 6.0.0 beta1リリース

6.0.0の機能は、だいたいこれまでウォッチで追ってきたとおりですね。


つっつきボイス:「おー、タイムラインが発表されてるし📅」「とりあえず餅の絵は描き終わったようだ😆」「4月末のRailsConf 2019にリリースって、もうすぐじゃん!: そんなに期間残ってないし、果たしてどのぐらずれ込むか😆」「もろもろ織り込み済みでしょうね☺

「そしてRails 6ではRuby 2.5以上が必須か〜: Rubyのバージョンアップが必要になることが増えるから大変そう😅」「気のせいか、以前のRailsよりRubyバージョンの要件の幅が狭くなっているような?🤔」「以前のRubyが結構切り捨てられてますね: まああまり古いRubyで動かすのも考えものだし」

「Rails 6のマルチデータベース、果たして今後落ち着いてくれるんだろうかとも思ったり🤔」「あー」「ま、いきなり使うことはなさそうだけど😆

⚓ActionCable::Connection::TestCaseが完全にマージ

# actioncable/lib/action_cable/connection.rb#L3
module ActionCable
  module Connection
    extend ActiveSupport::Autoload
    eager_autoload do
      autoload :Authorization
      autoload :Base
      autoload :ClientSocket
      autoload :Identification
      autoload :InternalChannel
      autoload :MessageBuffer
      autoload :Stream
      autoload :StreamEventLoop
      autoload :Subscriptions
      autoload :TaggedLoggerProxy
+     autoload :TestCase
      autoload :WebSocket
    end
  end
end
# 同PRより: 利用例
class ConnectionTest < ActionCable::Connection::TestCase
  def test_connected_with_signed_cookies_and_headers
    cookies.signed["user_id"] = "456"

    connect headers: { "X-API-TOKEN" => "abc" }

    assert_equal "abc", connection.token
    assert_equal "456", connection.current_user_id
  end

  def test_connected_when_no_signed_cookies_set
    cookies["user_id"] = "456"

    assert_reject_connection { connect }
  end

  def test_connection_rejected
    assert_reject_connection { connect }
  end
end

つっつきボイス:「Action Cableのテストがひととおり入ったのね☺」「入ったヘルパーは思ったほど長くないですね」「長々と書く意味ないですし😆」「😆

⚓親ディレクトリの監視を止めた

# activesupport/lib/active_support/evented_file_update_checker.rb#L125
      def directories_to_watch
-       dtw = (@files + @dirs.keys).map { |f| @ph.existing_parent(f) }
+       dtw = @files.map(&:dirname) + @dirs.keys
        dtw.compact!
        dtw.uniq!

        normalized_gem_paths = Gem.path.map { |path| File.join path, "" }
        dtw = dtw.reject do |path|
          normalized_gem_paths.any? { |gem_path| path.to_s.start_with?(gem_path) }
        end
        @ph.filter_out_descendants(dtw)
      end

つっつきボイス:「y-yagiさんの改修です」「指定のディレクトリが存在しないとEventedFileUpdateCheckerが親ディレクトリをチェックしてたのか: そういえば今日ちょうどチームでこのあたりが話題になってた🥺」「node_modulesまで対象に加わってたんですね」「確かに遅くなるやつ」「社内SlackのJavaScript板にこの間貼られてたこれ↓を思い出しちゃいました🦆

⚓render_templateの引数が変更された

# actionview/lib/action_view/renderer/streaming_template_renderer.rb#L46
-   def render_template(template, layout_name = nil, locals = {}) #:nodoc:
+   def render_template(view, template, layout_name = nil, locals = {}) #:nodoc:
      return [super] unless layout_name && template.supports_streaming?

      locals ||= {}
      layout   = layout_name && find_layout(layout_name, locals.keys, [formats.first])

      Body.new do |buffer|
-       delayed_render(buffer, template, layout, @view, locals)
+       delayed_render(buffer, template, layout, view, locals)
      end
    end

つっつきボイス:「tenderloveさんの改修です」「お?render_templateが変更されてるし…」「インスタンス変数で渡すのをやめたということでしょうか?」「逆ですね: インスタンス変数を渡すようになった」「あっと失礼しました💦」「従来はビューのコンテキストから@viewを取っていたのを、改修後は引数経由で取るようになったというということですね🧐

# actionview/lib/action_view/renderer/template_renderer.rb#L5
module ActionView
  class TemplateRenderer < AbstractRenderer #:nodoc:
    def render(context, options)
+     @view    = context
      @details = extract_details(options)
      template = determine_template(options)

      prepend_formats(template.formats)

      @lookup_context.rendered_format ||= (template.formats.first || formats.first)

-     render_template(template, options[:layout], options[:locals])
+     render_template(context, template, options[:layout], options[:locals])
    end

「この部分のAPIインターフェイスが変わるということだから、render_templateを生で書いてた人たちは影響を受けるかも?🤓」「あー🤭」「render_templateはRailsの内部向けのものだから、テンプレートを直にいじることは普通そんなにありませんけどね☺: その代り、ビュー周りを扱うgemたちが影響を受ける可能性はありそうTemplateRendererクラス↑も変わってるし🤔」「そっかー😳: そういえばこの記事↓ではAction Viewの基本設計はこれまでほとんど変わってなかったと解説されてました」「gemによってはデカい変更かも⚡

Railsのテンプレートレンダリングを分解調査する#2 ActionView編(翻訳)

なお、上の記事を書いた@st0012さん(Gobyの作者)にもこの変更のことを知らせたところ、「100%納得いく変更🥰」とのことでした。

「ついでですが、ivarって何だろうと思ったらインスタンス変数の略記でした↓」「果たして一般に通じるのかと😆」「Railsだけなのかな😆: instance variableってやっぱ長いですよね」

参考: ruby – What is ivar in Rails controller? – Stack Overflow

⚓ActiveRecord::StatementCache::Substituteのキャッシュのバグを修正

# activerecord/lib/active_record/relation/query_attribute.rb#L
      def unboundable?
        if defined?(@_unboundable)
          @_unboundable
        else
-         value_for_database
+         value_for_database unless value_before_type_cast.is_a?(StatementCache::Substitute)
          @_unboundable = nil
        end
      rescue ::RangeError
        @_unboundable = type.cast(value_before_type_cast) <=> 0
      end

問題: ステートメントキャッシュ(findとかfind_byとか)で使うステートメントをビルドするときに、ActiveRecord::StatementCache::Substituteオブジェクトがその属性の型のcastメソッドに渡される。キャスト呼び出しの戻り値はさして重要ではないが、このときに例外が出ないことを当てにしている。たとえば、ActiveRecord::StatementCache::SubstituteオブジェクトでActiveModel::Type::Float#cast_valueto_fを呼ぼうとすると例外が発生する。これはカスタム属性の型で問題になる可能性もある(PRとは別に含めたリグレッションテストを参照)。
解決: Active Modelの属性の型ではActiveRecord::StatementCache::Substituteオブジェクトを扱うべきではないので、ActiveRecord::Relation::QueryAttribute#value_for_databaseが北ときはsuperを呼ばないようにした。nil?メソッドでそうしたオブジェクトの型チェックが既にあったので、このクラスではそうしたクラスで既に特殊なキャストも行われていたことになる。
同PRより大意


つっつきボイス:「とりあえず普通にバグ: 使う人はビビるだろうな😨」「5b6daffのデグレだったとkamipoさんがフォローしてますね」

# activerecord/lib/active_record/relation/query_attribute.rb#L25
-     def boundable?
-       return @_boundable if defined?(@_boundable)
-       nil?
-       @_boundable = true
+     def infinite?
+       infinity?(value_before_type_cast) || infinity?(value_for_database)
      rescue ::RangeError
-       @_boundable = false
      end

-     def infinite?
-       infinity?(value_before_type_cast) || boundable? && infinity?(value_for_database)
-     def unboundable?
+       if defined?(@_unboundable)
+         @_unboundable
+       else
+         value_for_database
+         @_unboundable = nil
+       end
+     rescue ::RangeError
+       @_unboundable = type.cast(value_before_type_cast) <=> 0
      end

「ついでに5b6daff↑を見ると、unless bind.boundable?if bind.unboundable?に改められてる」「unlessifに変えて読みやすくしたんでしょうね」「infinite?と同じロジックで扱えるようにunboundable?にしたのね: まあ言い回しレベルかな」

Rubyにおけるunlessとコードの読みやすさについて

⚓Rails

⚓新しい「Zeitwerkコードローダー」をRails 6で導入の動き(Rails公式ニュースより)

Zeitwerk: 時間(time)と仕事(work)に相当するドイツ語

Zeitwerkという高級時計メーカーがあるんだそうです。「時の工匠」的な?


つっつきボイス:「これが例の新しいオートローダー」「ツァイトヴェルクっていう響きがいかにもドイツ語🇩🇪」「Zで始まる英単語ってあんまりないし😆」「そういえばドイツ語だとZの利用頻度がめちゃめちゃ高くて、逆にYがほとんど使われないのでキーが入れ替わってますね↓」

参考: Mac OS と iOS の融合 |  Mein dritter Blog — ドイツ語のキーボードレイアウトが解説されてます

「Mediumの記事では、Zeitwerkは高効率かつスレッドセーフのRubyローダー、とある」「known gotchaとあるのは、Railsガイドに載ってる今のオートローダーのハマりポイント↓ですね」「たしかにRailsのオートローダーはハマるときはハマるからな〜😭」「いつぞやの名前空間地獄とかですね😱

「で、Railsでは従来const_missingが使われていていろいろ限度があったけど、ZeitwerkではKernel#autoloadを使ってるのか」「Mediumの記事には古いローダーは2004年から使われてきたと書いてますね」

Railsの旧来のローダーはconst_missingを使っているため、ネストや、定数が見当たらない場合に使われるアルゴリズムをコールバックで取れないという根本的な限界がある。旧ローダー自身が持つ情報を使う分には間違いなく最適であり、2004年からずっとうまく動いていた。しかしRuby言語に新機能が加わったことで、さらに優れたアプローチが可能になった。
Zeitwerk gemはKernel#autoloadをベースにしており、ここ数年の私にとってはまさしく聖杯であった。
同記事の抜粋・大意

参考: const_missing — ActiveSupport::Deprecation::DeprecatedConstantAccessor

「Railsの既存のオートロードの仕組みってめっちゃ古いままだったのね👴」「Kernel#autoloadってRubyのメソッドでしょうか?新しいのかな?」「ざっとググってみると、Kernel.#autoloadはRubyの1.8.6とかには入ってるらしい」「そんなに前から!😳」「最初のRailsができた頃には既にあったのかもしれないけど、これまでKernel.#autoloadが活用されてなかったってことなのかな?🤔: 何にしろオートロードの仕組みが今後変わるとしたらデカい🗻

参考: module function Kernel.#autoload (Ruby 2.6.0)
参考: requireとrequire_relativeとautoload – Qiita

「まーrequireとか何も気にしないで使えるようになればいいことだし☺」「まだRailsに入れる作業は進行中なのか…」「Zeitworkのリポジトリも見てみるか: ネステッドrootディレクトリにも対応とか、loader.setupでセットアップできるとか↓」

# 同リポジトリより
loader.push_dir(...)
loader.push_dir(...)
loader.setup

「お、loader.reloadリロードもできるし、eager loadingもプリローディングもできるし↓: いいじゃないですか〜❤

loader.reload 

loader.eager_load

loader.preload("app/models/videogame.rb")
loader.preload("app/models/book.rb")

「inflection、つまりクラスとかの単数形複数形の活用も対応してますね↓」「どことなく想定外の動きをしそうな匂いがしそうではあるけど😅、カスタムinflectorもあるというのが何ともRailsらしい: つかRailsのオートローダーは活用形周りも扱ってるはずだから、確かにこういうこともできないと」

user             -> User
users_controller -> UsersController
html_parser      -> HtmlParser


# frozen_string_literal: true
class MyInflector < Zeitwerk::Inflector
  def camelize(basename, _abspath)
    case basename
    when "api"
      "API"
    when "mysql_adapter"
      "MySQLAdapter"
    else
      super
    end
  end
end

「名前空間になるクラスやモジュールはclassmoduleキーワードで定義しないといけない」「Trip::Geolocationは名前空間を生書きしてるんでしたね」

# trip.rb
class Trip
  include Geolocation
end

# trip/geolocation.rb
module Trip::Geolocation
  ...
end

「この書き方↓はできないと: この記法では普通でもオートロードはサポートしないし、納得」

# trip.rb
Trip = Class.new { ... }  # NOT SUPPORTED
Trip = Struct.new { ... } # NOT SUPPORTED

「Zeitwerk自体Ruby 2.4.1以上が必要ということだから、もしかするとそれでRailsが2.5以上必須になったのかも?🤔

「これがZeitwerkを作った動機かー↓」「今さらですけど、requireがグローバルということは、特定のファイルでだけrequireしたりできないんですね?」「ですです: Rubyではできません😅

requireの副作用がグローバルであるがゆえに、自分が依存するファイルのコードを読み込むrequireの呼び出しが完了したかどうかを安定して検証する方法が存在せず、実際一部が呼び出されてないなんてことが簡単に発生する。そしてこれのせいで、読み込み順に依存するバグが引き起こされる。Zeitwerkは、自分のコードでrequireのことなんか忘れて普通に書けばいいようにする方法を提供する。
その一方、Railsのオートロードはconst_missingベースであり、実際に使われた解決アルゴリズムやネストといった基本的な情報を欠いている。このせいで、RailsのオートロードはRubyのセマンティクスと一致することができず、さまざまなハマりポイントを生み出している。このプロジェクトは元々、改善されたオートロードをRails 6にもたらそうと思って始めたものである。
同リポジトリより大意

「Zeitwerkをざっと眺めてみて、必要そうなものはひととおり揃ってそうな感じ」「何だか期待できそうですね😍」「とにかくRailsの既存のオートローダーってたまにものすごいハマり方するから😭」「オートローダー地獄、何とかしないといかんですね…🥺」「あれは知ってても脱出が難しい、ホント😇

⚓開発者目線から見たタイムゾーン


つっつきボイス:「データベースとRailsとJavaScriptについてタイムゾーンを説明してるようです」「それぞれのタイムゾーンの設定が噛み合わないと大変なことになるみたいな話でしょうね🤕: 実によくある話」

「そういえば今のRailsは全部UTCに揃えるんだったけか?: configでタイムゾーンを変更しても保存はUTCになってた気がする」「取り出して使うときにローカルのタイムゾーンにするということなのかなと想像しました」「Railsで保存したタイムゾーンはRubyの世界では参照可能になるんですが、後で変更が効かない部分もあるんで厄介なんですよ」「えー😱」「システムタイムゾーンが違うとつらいことが起きたりします😇: JSも然り」

「LinuxのサーバーってUTCで運用するのが普通だったりするんでしょうか?」「んなことはない😆: システムタイムゾーンがJSTとか普通にあります」「そっかー」「だからややこしくなってしまうんですが、UTCのタイムスタンプが取れないシステムはさすがにないので、その点はマシ」

⚓Ransackか生SQLか


つっつきボイス:「jnchitoさんのスライドで、どこまでRansackでやってどこから生SQLにするかみたいな話をしてました」「これは自分もよく同じ話をしてますね: スライドでも『9割はRansackでいけるけど』とあるようにRansackで頑張りすぎるぐらいなら生SQLにする方がいいし」「いつぞやのRansackでOR使うと死ねるという話もありましたね」「複雑になるなら生SQLの方が全然いいし☺: スライドでやってるみたいにQuery ObjectとかFormクラスとか使ったり、conditions自分で書いたりとかして」

「お、『SQLはERBに書く』方針か、うーんなるほどー😎」「え、それってあり?と思ったら『Railsのビューとは関係ない素のERB』でしたか😅」「言われてみれば、最終的に完全なSQLができるならERBのような何らかのテンプレート言語を使って書くというのは悪くないですね: いろいろ融通が効くし、自分もちょうど昨日muninのコンフィグをERBで書いたりしてたし」「へー!」


munin-monitoring.orgより

「いいスライド👍」「🥰

⚓2019年の今、RailsをWindows 10で動かす(Hacklinesより)


つっつきボイス:「Windows 10でRails、割と動きますけどね」「昔は大変だったみたいですね」「むしろDockerとかの方が言うこと聞いてくれない😆: で、この記事はというと…なーんだ、WSLでやってるのか」「ほんとだ」「WindowsにネイティブにインストールするんじゃなくてWSL使うんだったら、たいてい動くんじゃないかなー普通に: 遅いとかあるかもしれないけど😆

Web開発環境をMacBook ProからWindows機に移行してみた話

⚓ActiveModelとActiveRecordの関係と、ActiveModel::Attributes

熱烈なファンのいるActiveModel::Attributesはもっと前からあってよさそうなのに、なぜこんなに最近になってできたのかが気になったので、Webチームの名メンターであるkazzさんに教わったことを中心にメモしてみました。

参考: ActiveModel::Attributes が最高すぎるんだよな。 – Qiita


つっつきボイス:「ちょっとメモするつもりが雑然としてしまったのでこの項スキップでもいいです💦」「あーそうそう、ActiveRecordとActiveModelって、インターフェイスはかなり似通っているのに直接の関係がないというのがややこしいんですよ🧐」「やっぱり〜」「そしてActiveModelは永続化のインターフェイスも少し持っているんですが、永続化についての責務は持たないというか永続化の実装がない🧐」「なるほど!」「スタブというか’口’だけを持ってるみたいな感じですね👄

「そうそう、ActiveModelはRails 3からだった: そしてActiveModel::Attributesが登場する前にもactive_attrとか、Dry-rbの前身だったvirtusみたいな似た感じのgemがあって、自分も使ってましたね😋

「そして今はActiveModel::Attributesがあるからこれでいいんじゃねって思うし😘: ActiveRecordは重いけど、ActiveModelは使いたいなんていうケースはあるかも」「ActiveModel::AttributesみたいなのってRailsでも何度かプルリクされてたんですね」「active_attrもそうですけど、みんな同じようなものを作ってたし😆

「実はActiveModel::Attributesって、アトリビュートを生やすだけなら実装はそんなに難しくなくて、自分で一度作ってみるととってもいい勉強になります🍖」「おー😍」「誰もが同じことをやってるぐらいだし、RubyでDSLをどう実装するかという練習にはぴったりだし、かつ実用的: ActiveModel::Dirtyみたいなことをするんでなければ大丈夫😉」「いいですね!😋

参考: ActiveModel::Dirty

追いかけボイス「ActiveModel::Attributesの登場は自分は遅いとは思わなかったなー: むしろ早い方だと思うし」

⚓Railsで巨大データを扱ってみた


つっつきボイス:「ぱっと見activerecord_importあたりを使うとか、ActiveRecordを通さないでやるとかそういう話かなと想像」

「ほほー、IOforeachのLazy Enumでこういう書き方↓できるんだ: だしかにlazyを通さないとサイズによってはメモリが溢れて死ぬだろうし😇」「CSVのエクスポートでやる方法も紹介されてますね」

# 同記事より
imported_products_old_ids = Work.pluck(:old_version_id).uniq

IO
  .foreach('tmp/export_products.txt')
  .lazy
  .map { |raw_line| JSON.parse(raw_line) }
  .select { |product_hash| imported_products_old_ids.include?(product_hash.fetch('id')) }
  .each do |product_hash|
    # ...
  end

YAML.storePStoreか↓」「どっちもRubyのライブラリなんですね: PStoreの方が速いっぽい」

# 同記事より
# export
require 'yaml/store'

def product_hash(product)
  hash = { id: product.id, id: product.title }
  hash.merge! product.media.map do |medium|
    { ... } #media data
  end
  hash.merge! product.comments.map do |comment|
    { ... } #comment data
  end
  hash
end

store = YAML::Store.new(Rails.root.join('tmp/products.yaml'))
store.transaction do
  store[:products] = []
end

Product.find_each do |product|
  store.transaction do
    store[:products] << product_hash(product)
  end
end

# import
store = YAML::Store.new(Rails.root.join('tmp/products.yaml'))
store.transaction(true) do
  store[:products].each do |product_hash|
      # ...
  end
end

「おー、PStore.newしてPStoreの形式でダンプしてロードできるっぽい: マーシャリング的なことを高速にやれるこういう仕組みがあったとは知らなかったなー☺」「おー」「たしかにYAMLとかに比べれば変換が発生しない分速そう❤

参考: class PStore (Ruby 2.6.0)

require 'pstore'
db = PStore.new("/tmp/foo")
db.transaction do
  p db.roots       # => []
  ary = db["root"] = [1,2,3,4]
  ary[0] = [1,1.5]
end

db.transaction do
  p db["root"]     # => [[1, 1.5], 2, 3, 4]
end

「元記事はSQLで頑張る方の話かと思ったら、どちらかというとRubyの世界で頑張る話か: こういうの社内に好きな人がいそう🥰

⚓ReactとRailsを統合する方法(Hacklinesより)


つっつきボイス:「APIサーバーで頑張るとかかな?」「やり方が3つ紹介されてました」

「1つはWebpackerかやっぱりー☺

「2つ目はreact_on_railsと」「これは前からあるヤツですよね?」「ですね: まあこういうのは今後どんなふうに変わるかわかりませんけどっ😆」「😆」「生で書く方がいいような気はするけどな〜」

「3つ目はやはりAPIサーバーとReactフロントですね」

⚓Ruby

⚓Pythonで欲しかったRubyの機能(RubyFlowより)

# 同記事より: Python
engineers = [e for e in employees if e.department == 'engineering'] # filter
engineer_salaries = [e.salary for e in engineers] # map
sum(engineer_salaries) # reduce
# 同記事より: Ruby
employees
  .select { |e| e.department == 'engineering' } # filter
  .collect { |e| e.salary } # map
  .sum # reduce

つっつきボイス:「いろいろわかりみはある: Pythonほとんど使ってないけど😆」「Pythonってprivateメソッドがないんですね: __付けて紳士協定的にこれはprivateだよみたいな感じでやってるみたい」「へー、privateがないとは😳

「Pythonは多重継承でやることが多い一方、Rubyはmixinでやれるともありますね」「Rubyだと式展開を引用符の中に置ける↓とか」「まあそういう違いはもうしょうがないっすね☺」「記事でも違いは微妙だけどと言ってました😌

# 同記事より: Python
"Hello, {}! Welcome to {}.".format(employee_name, company_name)
# 同記事より: Ruby
"Hello, #{employee_name}! Welcome to #{company_name}."

Pythonの「モジュール」は個人的にちょっと好きです😘。名前空間が他と別になっているあたりとか。

参考: 6. モジュール (module) — Python 3.6.5 ドキュメント

⚓自分が作ったgemをバズらせる方法(RubyFlowより)


つっつきボイス:「技術畑のマーケティング話的な」「READMEとかのドキュメントをちゃんと書いて、ドメイン取ってWebサイトも立てて、コミュニティを盛り上げる方向に頑張ってと、いたって王道な感じ☺」「もうこういうのが普通なんでしょうね」

「今のオープンソースはだいたいこういう感じでやってますね: 最初に立ち上げるものをキレイに作っておかないとものが良くても見向きもされなかったりするし、逆に見た目優先で立ち上げてもそこに人が集まってきて後から実体が伴ってくるなんてこともあるし☺」「☺

⚓Rubyのメソッドブロックを取り出してみた(Hacklinesより)

短い記事なので結果を載せます。

# 同記事より
require_relative 'block_source'

def foo
  pp block_source
end

foo { 'hello' }   #=> " { 'hello' }"
foo { |i| i * 3 } #=> " { |i| i * 3 }"
foo               #=> nil

つっつきボイス:「お〜〜ブロックのソースを抜き出すなんて、そんなことができるとは!」「Ruby標準の機能にはなさそうですね」「ないんじゃないかな🤓: 今動いているコードの行を引っ張ってくる機能は最近のRubyにあったと思いますが」「あーそうでしたっけ」「今動いているコードがソースのファイルの何行目なのかを知るみたいな機能をRubyKaigiで見た気がする」「デバッグで便利そうですね😋

探してみたのですがうまく見つけられませんでした🙇

参考: instance method IO#lineno (Ruby 2.6.0)
参考: Ruby で今読み込んでいるファイルの行数を取得する – Qiita
参考: 変数と定数 (Ruby 2.6.0) — 疑似変数__LINE__で取れるようです。

「お、caller_locationslineno↓を使ってソースを取り出してるっぽい: Railsコンソールとかでこういうの使いたくなることがあるかも😋

# 元記事より
module Kernel
  # RUBY_VERSION >= '2.6.0'
  def block_source
    @file_specs ||= {}
    bl = caller_locations.last
    source = @file_specs.dig(bl.path, :source) || File.readlines(bl.path)
    @file_specs[bl.path] ||= { source: source, ast: RubyVM::AbstractSyntaxTree.parse(source.join) }
    node = find_node(
      ast: @file_specs.dig(bl.path, :ast),
      type: :ITER,
      lineno: bl.lineno
    )
    extract_source(node: node.children[1], source: source) if node
  end
end

参考: module function Kernel.#caller_locations (Ruby 2.6.0)

「あれ?この記事書いた人のアバター見たことあると思ったら安川さんのところの人だ」「沖縄在住ですね☺

⚓Rubyist Magazine 0059号がリリース

るびま応援いたします❤

その後リリースされました🎊🎉。Ovto面白そう!

⚓その他Ruby


つっつきボイス:「伽藍とバザール、懐かし!」「90年代でしたよね」「今や古典📚」「MINIXのタネンバウムがLinuxを巡ってリーヌスとやりあった話とか思い出しちゃいました」

参考: 伽藍とバザール – Wikipedia
参考: アンドリュー・タネンバウム – Wikipedia
参考: MINIX – Wikipedia

⚓Ruby trunkより

⚓卜部さんが手がけたRuby高速化

trunkではありませんが。


つっつきボイス:「今朝電車の中で読んでて高速化の追い込みがスゲーと思ったので: エン・ジャパンのエンジニアHubはいい記事が多いなと改めて感じました😍」「あそこはかなり頑張っていい記事書いてますね👍」「シンタックスハイライトがスマホで読みにくかったぐらいでした😅」「まあそのぐらいは😆: コードをスマホで読むのもどうかと😆」「ですね😆

⚓クラウド/コンテナ/インフラ/Linux/Serverless

⚓Podman: Docker互換の新コンテナエンジン(Publickeyより)


同サイトより

とりあえずかわいい❤


つっつきボイス:「おーこういうの出てましたね: Red Hatは元々Dockerに相当つぎ込んでるし、わかる気はする」「おー」「PodmanってDockerエンジンと差し替えられるのかな?」「デーモンがないのと、KubernetesのPodもサポートしてるのが特徴みたいですね」「だからPodmanなのかな?知らんけど😆

「OCI↓に対応してるというのも筋がよさそう: 後はみんながどれだけ使ってくれるかですが☺」「Dockerに追いつけ追い越せの気合いがPodmanのマスコットの可愛らしさに表れてる気がしました🐹」「Dockerと完全互換ならワンチャンあるかもしれないけど😆

参考: Home – Open Containers Initiative

「PodmanのRootlessコンテナもかなりありがたい🙏」「ということはDockerはそうじゃないんですね?」「です: DockerはDockerデーモンが基本的にはroot権限で動くので」「権限強すぎな感じですね…」「Dockerの中のファイルのUIDはDockerホスト側のUIDと同じもので作ることを強いられるので、root権限で動かすか権限昇格するかしないとファイルを作れなかったりするし😅」「なるほど!」「Dockerはそこが不便なんですよね😢: 特に-vとか付けるとDockerホストを汚染する可能性があって危険⚠」「ほあっ😨」「だからKubernetesとかの方が分離しやすかったりしますね」

⚓SQL

⚓AWS DocumentDBはほんとはPostgreSQLじゃね?(DB Weeklyより)


つっつきボイス:「AWS DocumentDBが実はPostgreSQLでできてるんじゃないかと睨んでるそうです」「内部的に同じものを使ってる可能性はまああるかもですが、ずばりそのものを使ってるんじゃないかという勢いで書かれてるっぽい☺」「っぽいですね」

「でもDocumentDBが自動でAvailability Zoneにコピーを6つ作成するなんてのは、データベースというよりはAuroraファイルシステムの特徴なので、その上にDocumentDBやPostgreSQLが乗ってるならそこが同じだとしても不思議じゃない」「おー」「ま、こういうものがフルスクラッチで書かれているかどうかとか気にしないけどっ😆

⚓最近のSQLiteは何だかスゴイ(DB Weeklyより)


つっつきボイス:「お、SQLiteにいつの間にかwindow関数が入ってるし」「マジっすか!?😳」「FILTER構文に、それからON CONFLICTって何だろう?」「衝突したらUPDATEできるみたいなヤツかな?UPSERTみたいな」「複数プロセスが開いているときの挙動とか?知らんけど」

-- 同記事より
INSERT INTO target
SELECT *
  FROM source
    ON CONFLICT (id)
    DO UPDATE SET val = excluded.val

「実はSQLiteってかなり頑張ってますね: 新しいSQLiteは相当ちゃんとしていて、結構いい❤」「おぉ〜」「実際使っててホント速いと思いました(参照のみだけど)」 「規模が小さければもうSQLiteでいいんじゃねというぐらいの勢い」「そうそう🥰

「SQLiteは特にクライアントで動くアプリで使うのに最適: 何てったって速いし🚅」「ですね〜」「サーバーだとどうしても同時アクセスが多数発生するから、それをちゃんと捌けるRDBMSが必要になるけど、クライアントアプリなら自分しか使ってないからそんな心配もいらないし🤓」「Webアプリでも、リードオンリーのマスターデータ専用に使う手もあるかも(やったことないけど😆)」


sqlite.orgより

⚓その他SQL


同リポジトリより


つっつきボイス:「ほーNodeで動くSQLデータベース」「SQLiteを使いたくても使えないような環境で使うとか?もしかするとGAS(Google Apps Script)でも動いたりして」「それありそう」「頑張るところが何か違う気もするけどっ😆」「😆

参考: Apps Script  |  Google Developers

⚓JavaScript

⚓私はいかにしてJSモジュールのデフォルトexportをやめたか(JavaScript Weeklyより)


つっつきボイス:「export default class LinkedList {}なんて書き方できるんだー: JSそこまで詳しくない〜😅」「exportをデフォルトでやって何かつらい目にあったのかしら?」「exports.LinkedList = class LinkedList {};みたいな名前付きexportもできるのか: 昔見た気もするけど」

「あーimportでこうやってas名前変えられると↓」「ないと困りそう」「importする側で何とかしてくれみたいな☺」「こういう文化の言語ってありますよね: Pythonとか」「Go言語もそうですね」「みんな同じような名前で作っちゃうという想定の世界」

// 同記事より
import { LinkedList as MyList } from "./list.js";

⚓JSでタイムゾーンを扱う(JavaScript Weeklyより)


つっつきボイス:「GMTって最近この業界ではめっきり見かけないな〜」「そういや見ませんね」「グリニッジ標準時でしたっけ: イギリスが覇権を失った感🇬🇧」「そしてサマータイムの話題も😆

参考: グリニッジ標準時 – Wikipedia

「そして恐怖のDateオブジェクト↓」「Local Storageにマーシャリングして保存した後で元が変わったりしたときにつらくなった覚えが: 変わってもいいように書いておけばいいんだけど、それを忘れて死んだりとか😇」「新しいタイムゾーンを読み込むとローカルで持っているデータがぶっ壊れたり🧨

// 同記事より
const d1 = new Date(1489199400000);
d1.toString(); // Sat Mar 11 2017 11:30:00 GMT+0900 (KST)

「Momentsって何だろうと思ったらライブラリでしたか」「Moments.jsといえば日時関連処理で結構メジャーですね🧐」「JSの日付時刻処理が非力すぎるからこういうMoments.jsとか昔流行ってた」


momentjs.comより

↑サイトでは時計が実際に動いています。

⚓TypeScriptなしで型厳密にやる方法(JavaScript Weeklyより)

割と短い記事です。


つっつきボイス:「TypeScriptなんかなくたって型厳密にやれるんだぜみたいな😆」「ランタイムでannotationを解決しながら実行するとかそんな雰囲気」「まあでもTypeScriptは中で実際にこういうことをしてるわけだし、手作りできるっちゃできるし」


typescriptlang.orgより

⚓wpk: Webpackの非公式CLI(JavaScript Weeklyより)


同リポジトリよりより


つっつきボイス:「アンオフィシャルなWebpack CLIとな」「オフィシャルのCLIってつらいんでしょうかね: いっそBundlerと一緒のコマンド体系だったらいいのに」「きっと逆も真なりで、Webpackを使ってるJSエンジニアもRubyに対してそう思ってるかも😆」「😆」「何にしろ公式に入ってくれないとどう変わるかわかんないし」

⚓sockette: 薄いWebSocketラッパー(JavaScript Weeklyより)


同リポジトリより

// 同リポジトリより
const Sockette = require('sockette');

const ws = new Sockette('ws://localhost:3000', {
  timeout: 5e3,
  maxAttempts: 10,
  onopen: e => console.log('Connected!', e),
  onmessage: e => console.log('Received:', e),
  onreconnect: e => console.log('Reconnecting...', e),
  onmaximum: e => console.log('Stop Attempting!', e),
  onclose: e => console.log('Closed!', e),
  onerror: e => console.log('Error:', e)
});

ws.send('Hello, world!');
ws.json({type: 'ping'});
ws.close(); // graceful shutdown

// Reconnect 10s later
setTimeout(ws.reconnect, 10e3);

つっつきボイス:「ロゴと名前はsocketとsocksの掛詞っぽいですね🧦」「なるほどカワイイ🐼」「これはWebSocketに限らずできるのかな?WebSocketが対象っぽい」「サイズが348バイト!」「えれえ小さい🐁!」「ソースも50行ちょい!」

⚓CSS/HTML/フロントエンド/テスト

⚓手動テストで必ずやってる4つのこと


つっつきボイス:「1つ目はブラウザでアプリをキーボード操作してみること」「アコーディオンがちゃんと開くかとか」「こんなふうに↓マウスオーバーで選択するタブがキーボードでも選択できるかどうかとか: 確かにこういうのは実際にやらないとわからないし: スクリーンリーダー使ってる人はそれできないと困るし」「ちょうど2番目がスクリーンリーダーの話ですね」「スクリーンリーダーのチェックも必要☺


同記事より

「4番目のWave Toolって知りませんでした」「おー、これはいわゆるWeb評価用ツールですね: こんな感じ↓で評価してくれるのか」


同サイトより

「Web評価ツールといえば、日本の官公庁が出してるアクセシビリティチェッカーがあるんですよ: 何て名前だったかな…miChekcer!↓」「へーこんなのがあったとは」「このmiChekcerというツール、恐ろしく古いんですが今もすごく使われているという」「官公庁向けだと使わないわけにいかないんでしょうね😢」「でないと納品できなかったり」「音声読み上げとか、そういうチェックも入ってるのね」「32ビットとか書いてますけど😆」「Javaのサポートが変わったお知らせも書いてある: ああ例のアレ」「ActiveX…だと…?🥶」「そもそもIEが対象だし」「ひえぇ〜」「こういうツールを公のところが出す意義は間違いなくあるけど、その後も更新してくれないとね😅

参考: ActiveX – Wikipedia
参考: 行政サイトを作る時に気をつけておいた方がいい事 – Qiita

⚓言語

⚓ミューテーション解析とは


つっつきボイス:「ひところ卜部さんがRuby TrunkすごくエッジなバグをRubyで次々にアップしてたことがあったんですが、もしかしてこういうミューテーション解析みたいに、ソースをランダムに機械的にちょっと書き換えてバグをあぶり出すとかしていたのかなと思って」「ああそういえばRubyKaigiでそういうのを自動で回してるみたいなことを言ってたかも🤔」「ミューテーション解析っていう言葉があるというのを今頃知りました😅」「これはまさにソフトウェアテストのコアな手法のひとつ」「パーサーの仕様を把握したら後は総当たりで回して、落ちたところを掘っていくみたいな」

⚓PPL 2019: 第21回プログラミングおよびプログラミング言語ワークショップ


つっつきボイス:「東北地方(岩手県花巻市)で開催されるそうです」「JSSSTということは日本ソフトウェア科学会↓なので普通に学会ですね🧐


jssst.or.jpより

「ちなみに、日本には日本ソフトウェア科学会の他に情報処理学会と電子情報通信学会という3つの大きな学会があります」「おー知らんかった😳


ipsj.or.jpより


ieice.orgより

「下の2つは歴史が長くて、情報処理学会は数学寄りでコンピュータサイエンスをガチでやってる」「なるほど!」「電子情報通信学会は名前からも想像できるように通信とか工学系も扱っていて、ネットワークのような下のレイヤにも強い」「へぇ〜」

「日本ソフトウェア科学会は比較的新しくて、いわゆる学術論文になりづらいようなテーマ(何かソフトウェアを構築したとか)でも提出できるところが他の2つと違う特徴ですね」「あーそういう違いですか!」「論文にはなりにくいけど意義のある研究というのはやはりあって、日本ソフトウェア科学会はそういうのも扱う」

「一方他の2つはソフトを書いたというだけじゃ論文や研究として認められないんですね」「下の2つはガチ勢という感じですか」「書きましただけじゃダメと」「学術の世界は、極端に言えば理論があればコードは実際には書かなくてもよかったりするので」「わかるわかる☺」「せいぜいシミュレーターとか」

「たとえばRubyを作りましただけでは、それは研究ではなくて開発だろとみなされる: 新しいアルゴリズムの研究とか、論文として新規性などを満たさないといけなかったりしますね🧐」「そうすると学生がたとえばRubyで何か凄いものを作ってもそのままではドクターが取れないことになってしまう😆

「日本ソフトウェア科学会はそういうのも論文として評価することで門戸を開いているので、オープンソース方面と親和性が高いですね☺」「学術の世界、知らないことだらけでした😅

⚓COBOLが去りゆく

代わりにPythonが加わるそうです。


つっつきボイス:「CASLを試験に出すぐらいならRuby入れればいいのに🤣」「🤣」「CASL、日本でしか通用しなくて実物もないという🤣」「ベンダに依存しないアセンブラの問題を出すための苦肉の策だったのかしら🤔」「知人はES2015を入れればいいのにと言ってました😆

参考: CASL – Wikipedia

「Rubyは言語仕様の動きが激しいから試験にしづらいとか?」「でもJIS規格でしたよね?」「ISOも取ってた気が」「お〜どっちもやってる↓」「更新もされてるんだし、Rubyにしとけばいいのに」「まさか試験に出るPythonってPython 2じゃないよね?🤣」「🤣

参考: JIS X 3017 「プログラム言語Ruby」が改正されました:IPA 独立行政法人 情報処理推進機構
参考: ISO/IEC 30170:2012 – Information technology — Programming languages — Ruby

後日

⚓その他言語

⚓その他

⚓「HRTの原則」とは

「このメソッドの制御フローは完全に間違ってますよ。みんなが使ってる標準的なxyzzyコードパターンを使うべきですよ」

Team Geek p.21

上のメッセージはもっとえげつない感じで訳す方が伝わったのかなと思いました。

なおHRTの原則を見かけたのは永和さんの記事でした。

参考: 知識差−スキル差を埋めるためのペアプロ+αのコツ(2) – 時を超えたプログラミングの道

⚓whatthecommit.com: コミットメッセージをひたすらランダムに表示するサイト


whatthecommit.comより: 「ボクを信じてくれればこんなことにならなかったのに」

以下で見かけました。


つっつきボイス:「リロードするたびに変わりますね」「いわゆる面白コミットメッセージ😆」「どこからクロールしてきたんだろうか😆」「そんなにたくさんはないっぽいですが☺

⚓ビデオ会議のノイズキャンセルアプリ


つっつきボイス:「Yasslabの安川さんの記事を見つけたので」「ノイズをキャンセルするアプリってどういう仕組なんだろ?🤔」「なるほど、このパネルからしてスピーカーやマイクのドライバとして選択すると機能するのか↓🎤」「ビデオ会議を頻繁に使うなら便利かも😋


同記事より

⚓番外

⚓宅配ロボット

参考: Amazonがついに宅配ロボットを開発! 名前は「Amazon Scout」 | ロボスタ


つっつきボイス:「名前はスカウト!」「階段はクリアできないことが確定😆」「道路事情からして日本は蚊帳の外🇯🇵」「弓矢やボウガンを持った一団にヒャッハー🏹される事案がちょっと心配😆

「でも米国だとこういう無人配達はある意味切実に求められてたりしますよね: 地域によっては他人の敷地にうっかり入ると撃たれることがたまにあったりするし🔫」「そういえば随分昔にそんな事件あった!😢」「宅配便や郵便配達も油断ならなかったりしそう😢

参考: 日本人留学生射殺事件 – Wikipedia


今回は以上です。

おたより発掘

こちらこそありがとうございます🙇

バックナンバー(2019年度第1四半期)

週刊Railsウォッチ(20190121)Rails 6.0.0 beta1リリース、Railsは2019年も「あり」か、Jetsでサーバーレス、ES2018の新機能、RSpecの心ほか

今週の主なニュースソース

ソースの表記されていない項目は独自ルート(TwitterやRSSなど)です。

Rails公式ニュース

Ruby Weekly

RubyFlow

160928_1638_XvIP4h

Hacklines

Hacklines

Publickey

publickey_banner_captured

DB Weekly

db_weekly_banner

JavaScript Weekly

javascriptweekly_logo_captured

Railsは2019年も「あり」か?#2 Railsの長所と向いている用途(翻訳)

$
0
0

Railsを使っているアプリ(2019年版)

実際のところ、相当たくさんあります。

訳注: 本セクション内の太字のサービスはいずれもRuby on Railsで動いています。

現時点のGitHubのユーザー数は全世界で2,200万人にのぼり、6,100万ものリポジトリを抱えています。コードリポジトリとしては世界最大規模です。

Airbnbも最初期からRuby on Railsを使っています。数百万ものユーザーが、世界65,000以上の地域にある部屋を毎月予約しています。

スタートアップの方ならBasecampという名前をご存知か、既にお使いかもしれません。Basecampは単にRuby on Railsを積極的に使っているのみならず、Ruby on RailsがBasecampで誕生したことを皆さんが知ったら驚くでしょう。さらに、Railsと相性のよいStimulusJSというクールなJSフレームワークも同社が開発したものです。

Shopifyといえばeコマースプラットフォームの巨人ですね。

私たちが最近手がけた、オークションのリアルタイム入札をオンラインで行うアプリの開発は実に興味深いものでした。このアプリではバックエンドにRuby on Rails、フロントエンドにVue.jsをそれぞれ用いましたが、魔法のように見事に動きました。詳しくはArtinfo – Portfolioをご覧ください。

GitHubHerokuCodeClimateTravisCIShopifyはいずれもRailsを用いています。Railsコミュニティの影響力の大きさは、数多のコミュニティ中でも過去最大です。他のコミュニティはいずれもRailsコミュニティを追いかけているに過ぎず、Railsコミュニティの力量によって達成されたこの成熟ぶりを他のコミュニティが追い抜こうにも、数年分は引き離されているのが現状です。
Fabio Akita, Akita on Railsより

皆さんが普段よくお使いのWebアプリのうち、バックエンドにRuby on Railsを用いている例なら、まだまだいくらでもリストアップできます。手短に言えば、Ruby on Railsは今も広く使われていますし、Railsのテクノロジーもそれなりに人気を集めています。別にRailsで作った途端に目覚ましいパフォーマンスを発揮するからではありません。パフォーマンスについては皆さんの使い方次第です。

Railsの長所

ここまでご覧いただいたように、RubyやRuby on Railsがこれまで人気を獲得してきたことには理由があります。Railsにはいくつもの強みがあり、そう簡単に超えられるものではありません。

1. 開発プロセスが短期間で済む

プロジェクトの開発を短期間に終えるという点において、Railsに敵うものはありません。MVP(Minimum Viable Product)アプリをリリースしたいスタートアップや企業にとって、Railsはまさにうってつけのテクノロジーです。Ruby on Railsではプロジェクトや製品を驚くほど短期間で、しかも容易に開発できます。Ruby on Railsでアプリを開発する場合、他と比べて40%は期間を短縮できます。

2. ビジネスロジックを実装しやすい

Ruby on Railsアプリでは、複雑なビジネスロジックを容易に実装できます。APIがなる早で欲しいときにも大丈夫。Rails開発者ならたちどころに作れます。あとはVueやReactなどのフロントエンドフレームワークでぺったんぺったんすれば、Webアプリの一丁上がりです。

3. ライブラリが豊富

「欲しいgemは必ずある」

Rubyでは、コミュニティで開発されたおびただしい数のgemライブラリが何かと称賛の対象になります。チーム開発では、マイナーだがたまに必要となる実装にいちいち気を取られずに、できるだけ実際のロジックに専念すべきです。gemのおかげで、さまざまな機能や、アプリと外部サービスをつなげる「ブリッジ」を簡単に実装できます。ありがたいことに、ほとんどのgemは無料で商用利用できます。

ある機能を開発していて、あるいはアプリで内で外部アプリを実装しようとして、もし行き詰まることがあれば、おそらくそれに適したgemが既にあるでしょう。Rubyのライブラリのおかげで開発者の苦しみの多くが取り除かれ、市場に出すまでに必要な期間が大きく短縮されます。

RubyやRuby on Railsがドンピシャリとはまる用途やプロジェクトは、実にたくさんあります。私たちが作った、Railsを自社アプリや製品で積極的に使っている企業のリストがこちらにありますので、どうぞご覧ください。

Railsに向いているプロジェクト

同じことを実現するにもさまざまなテクノロジーが使えますが、その中から最適のものを選び出すのはとても困難です。そういうわけで、ささやかながら私たちがRuby on Railsを選ぶ以下のカテゴリリストを作りました。

1. 「普通の」Webアプリ

Ruby on Railsは、普通のWebアプリ向けのソリューションとして今もよい選択肢です。ユーザー数百万とか巨大トラフィックとかを期待しないのであれば、Ruby on Railsが向いている可能性があります。さまざまなデメリットにもかかわらず、Railsのテクノロジーには多くのアプリを支えてきた実績と信頼があります。

2. eコマース

先ほど例に挙げたShopifyを思い出しましょう。eコマース界の巨人であるShopifyを支えているのは、愛しのRuby on Railsフレームワークです。Rails製のeコマースフレームワークといえば、Spree Commerceもお忘れなく。

eコーマス機能で必要になりそうなgemがひととおりあるので、Ruby on Railsですぐにでも自分の店舗を簡単に立ち上げられますし、支払い用ゲートウェイ、マーケティングメールでのキャンペーン、ヘルプデスクなどもセットアップできます。

3. カスタムデータベースソリューション

Ruby on Railsフレームワークは、画期的な新ビジネスモデルに欠かせない高度なデータベース構造の扱いに長けています。Railsフレームワークには優秀なORM(Object Relational Mapping)であるActive Recordが備わっているので、開発者はSQLを使わずにデータベースを簡単に操作できます。何より、RailsではPostgreSQLなどのデータベースマネージメントシステムをスムーズに統合できます。

4. CMS(コンテンツマネジメントシステム)

Ruby on Railsのエコシステムには、SEOで使いやすい優秀なCMS向けツールが多数あります。コンテンツ中心のWebサイトがふさわしいとお考えの場合は、Jekyllをお試しください。あなたが今読んでいる私たちのサイトもJekyllで作られています。NetlifyとJekyllの合わせ技について詳しくはこちらをご覧ください。

5. コンセプトが定まる前のアプリ

Ruby on Railsでアプリを構築していてありがたい点は、事前にみっちり計画を立てなくても、開発を進めながら機能を追加できることです。Railsが多くのスタートアップに重宝されている理由がこれです。

6. プロトタイプが急遽必要な場合

Ruby on Railsがプロトタイプ作成を得意している点も、重要なメリットのひとつです。最小限ながらも実用的な機能を驚くほど短期間に開発できます。MVPアプリを作ることで、ユーザーが求めているものや、ビジネスで着目すべき点を明らかにできます。

Ruby on Railsとプロトタイプ作成

歴史を振り返れば、Railsでビジネスを始めたスタートアップは、Twitter、Airbnb、Hulu、Netflixなど枚挙に暇がありません(詳しくはこちらをどうぞ)。その後ビジネスが拡大して別のフレームワークに乗り換えたスタートアップもあれば、Railsを使い続けているスタートアップもあります。スタートアップはRailsの力を得ることで、小規模アプリやMVPアプリを時間をかけずに構築できます。短期間で開発を終えることで、その分多くのユーザーを早いうちに獲得し、アプリの収益増大を早めることもできます。

ここで肝心なことは、Railsのいくつかの限界と折り合いをつけることができるならば、「SNS」から「オンラインショップ」「オークションサイト」「情報システム」に至るあらゆるアプリを、現在はもちろん将来に渡って開発できるようになるという点です。もちろんRubyやRailsは決してありとあらゆる仕事をこなすわけではなく、できる仕事はほんの一握りですが、その仕事を実に見事にやってのけます。


関連記事

Railsは2018年も現役か?: 前編(翻訳)

Railsは2019年も「あり」か?#3 短所と不向きな用途、他の選択肢など(翻訳)

$
0
0

Railsの短所

不運にもRuby on Railsの人気に陰りが生じつつありますが、人気の下落にはそれなりに深刻な理由がいくつかあります。理由が致命的なものとは限りませんが、ものによってはプロジェクトで厄介な問題を引き起こす可能性もありえます。

1. パフォーマンス

これについては触れないわけにはいきません。Railsはスピードにおいて強者ではありません。処理速度が極端に高く、サーバーのメモリ消費も少ないものが必要な場合、Ruby on Railsは明らかに不向きです。もちろん、これはあくまで極端な場合であることを忘れてはなりません。数百万ユーザーをさばくことを期待しているのでもない限り、ほとんどのプロジェクト(特にスタートアップやMVP)ではそんなスピードを必要としません。ユーザーは、便利なアプリを安全かつ直感的に使えているうちは、パフォーマンスのことなど気にしないものです。

2. スケーリング

Twitter vs Railsは忘れがたいドラマでした。当時「Railsはスケールできない」といった議論が噴出したものです。ただしRailsは実際にはスケール可能です。サーバーのRailsをアップグレードすることも、複数のサーバーやツールで処理を手分けすることも可能です。しかしそうする価値はあるのでしょうか?こればかりはアプリ次第です。

前述のとおり、Railsのテクノロジーはバックエンドとしてそこまで高性能ではないと考えられています。他のフレームワークやバックエンドソリューション並のパフォーマンスをRailsで得ようとすれば、もっと性能の高い本格的なサーバーインフラに莫大な資金を注ぎ込まなければならなくなるでしょう。Node.jsやPhoenixといった高性能なテクノロジーを選択すれば、月に数千ドルは節約できることもあります。

3. 柔軟とまではいかない(個人の感想です)

Ruby on Railsフレームワークを使い慣れている方からすれば、これはものすごく偏った意見に見えるかもしれません。ここでいう「柔軟とまではいかない」とは、「Rails様は、Rails wayに乗ったアプリしか’お望み’にならない」ということです。この方針は多くの場合非常にうまくいっていますが、ひとたびRails wayから外れたありきたりでないアプリを開発しようとすると、たちまちつらくなることもあります。Railsにはデフォルト値やオブジェクトの詰め合わせセットなどがずらりと揃っているので、開発者が独自の創意を発揮する余地があまり残されていないように見えるかもしれません。もちろん開発者が「オレのやりたいようにやらせろ」と心ゆくまでオリジナリティを発揮しようと思えばできますが、その分開発期間も圧迫されるので、開発期間とオリジナリティを天秤にかけなければならなくなります。

4. 用途によっては「オーバーキル」

ここで大事なことを指摘しておきます。RailsがなくてもWebアプリは作れるのです。私たちは、プロジェクトの種類に合わせて最適なテクノロジーをお客様におすすめしています。お客様の要件によっては、Railsだと少々トゥーマッチなこともあれば、別の既存ツールならどんぴしゃりとはまることもあります。

5. Ruby言語と機械学習

AI(人工知能)やML(機械学習)はここ4年ほどホットな話題に顔を出しています。今どきのアプリの大半は、何らかの形で機械学習と統合されており、退屈な仕事を手伝ったり、主に既存の業務や人員をコードで置き換えることで業務を自動化したりしています。

この分野は、一言で言うと残念ながら今のRuby言語には荷が勝ちすぎます。ALやMLとくれば、Pythonが最適なテクノロジーです。言うまでもなく、Pythonは世界中で使われている人気上位のプログラミング言語であり、Rubyよりずっと高速です。Javaですら、AI/MLに適しているテクノロジーとみなされています。惜しいことに、愛しのRubyは主に必要なライブラリの不足が原因で、MLというもうひとつの大きなトレンドをフォローできていません。もちろん、言語というものはどんな用途にも使える万能性までは想定されていないことを忘れてはなりません。したがって、これはさほど大きな問題ではありません。

Railsを使うべきでない用途

1. 自社紹介に毛の生えたようなサイト

わざわざ不要な複雑さを持ち込まないようにしましょう。Ruby on Railsは、シンプルな静的サイトを作るには少々トゥーマッチです。そうした特定の用途であれば、コーディングをほとんど必要とせずにやれる選択肢がいくらでもあります。

2. ブログ中心のサイト

その気になればRailsでブログWebサイトを15分で作れることは、皆さんもデモでとっくにご存知かと思いますが、その後Webの世界ではさまざまなものが登場しました。今なら使いやすいツールの揃った本格的なブログを無料で構築できますし、そのためにRuby on Railsという戦車をわざわざ繰り出す必要もありません。

3. 開発者がRailsに不慣れな場合

開発者が好きでもないテクノロジーや、開発者が不慣れなテクノロジーを強要してはいけません。開発者がGo言語やNode.jsなどのバックエンドフレームワークに通じているのであれば、それを使ってアプリを開発してもらいましょう。

4. 納期がなく、プロトタイプも不要な案件

ここは重要です。既に何度も申し上げたとおり、Ruby on Railsは開発者の生産性を最大限に高めることができるがゆえに、開発期間を大きく短縮できます。残念ながら、Railsはその過程でパフォーマンスの一部を犠牲にしています。納期が明確に定まっておらず、MVPアプリも不要であれば、他のバックエンドソリューションを検討すべきです。もちろん、アプリの要件やその他の要素によって変わってきますが。

Rails以外のRubyバックエンドの選択肢

1. Sinatra

Sinatraは、Ruby on Railsに代わるミニマムな選択肢です。Sinatraの作者は、Railsとは少しばかり異なるアプローチを採用しており、アプリのセットアップに必要なコンポーネントを絞り込んでいるので、読み込みが比較的高速です。

2. Hanami

何らかの理由でRuby on Railsを使うわけにいかなくなった場合は、Hanamiをお試しください。HanamiはRuby製の新しいフレームワークであり、Ruby on Railsの短所のいくつかを修正することを保証します。Hanamiのメモリ消費量はRailsの60%に抑えられており、セキュアかつ応答も高速で、かなりの程度の軽量化を保証します。私たちが調べた範囲では、Hanamiは、Ruby on Rails以外では初めてと言える「本物の」Rubyフレームワークの選択肢です。詳しくはHanamiRBをどうぞ。

3. サーバーレスRuby

本記事を書いている間に、何やら面白いことが起きました。「サーバーレスで使える」Rubyをぜひチェックしましょう。AWSはつい先ごろ、AWS LambdaでRubyをサポートすると公式にアナウンスしました。これはRubyコミュニティにとって一大ニュースです!Java、Node.js、Go言語、C#などと同じくAmazonのクラウドにRubyも参入しました。

「サーバーレスRuby」にはどんな意義があるのでしょうか?開発者はサーバーインフラのことを気にしなくてよくなりますし、コードを正しく書きさえすれば、アプリの特定の機能をAmazonのクラウドの計算機パワーの一部として実行できるようになります。支払いが計算時間ベースだけで済むのもクールです。

AWS Lambdaやその他のサーバーレステクノロジーの登場が比較的新しいので、サーバーレスのコンセプトに精通した開発者や、サーバーレス経験のある開発者を見つけるのは今の段階では一苦労です。

AWS Lambdaについて詳しくはこちらをどうぞ。

GitLabもRubyを使っている

少し前の話ですが、GitLabは同社のRuby on Rails開発についての打ち明け話を記事で公開しました。GitLabは、リポジトリのストレージとしては世界2位の人気を得ており(異論はあるかと思いますが)、そのバックエンドではRuby on Railsが使われています。同社CEOのSid Sijbrandijは、自分たちが最初にRuby on Railsを選択したのはまさしく先見の明であり、そのおかげでさまざまな高品質の機能を苦もなく実現できていると述べています。

それだけではありません。GitLabの開発チームはさらなる課題への挑戦を続けています。コードの一部をGo言語で書き換えたり、Vue.jsフレームワークを採用したりすることで、パフォーマンスの問題を克服しました。

Ruby言語の未来とは

遡ること数年前、Rubyプログラミング言語のクリエイターであるMatzことまつもとゆきひろ氏は、「Ruby 3×3」と銘打った新しいバージョンのRubyを手がけていると発表しました。このプロジェクトではRuby 2.0の3倍の高速化を目指しています。リリース予定日は残念ながら未定ですが、2020年終わりまでのリリースとしてスケジューリングされるであろうと期待されています。

Ruby世界は1つのWebフレームワークで終わる。RailsはRubyを活かすと同時に苦しめてもいる。
Grzegorz Wilczyński(Lunaremのソフトウェアエンジニア)

Rubyファンは、Ruby 2.6のプレビュー版でRuby 3×3の一部が既に導入されていることに気づいています。JITコンパイラの導入によってパフォーマンスが著しく向上したことがさまざまなベンチマークで示されました。もちろん、変わるのはスピードだけではありません。Matzのインタビューによれば、Ruby 2.6はクリスマスのリリースが予定されており、ある種のコンカレンシー抽象化もリリースに含まれます(訳注: 2.6は予定どおりリリースされました)。

Matzは他にも、過去のRubyアプリケーションではRubyのバージョンが変わると互換性が失われていたと述べています。かつてMatzはさまざまな変更を導入して「巨大なギャップ」が生じ、それによってRubyコミュニティが分裂したことがありました。Matzは今後そうした間違いが起きないようにするとのことです。Matzは大胆にも、Ruby 2のプログラムはすべてRuby 3で動くようにすると述べています。

StackOverflow Developer Surveyでは、Rubyを愛する開発者が減少しつつあるという残念な結果が出ていました。実際、トップ10にも入っていません。Matzは、現状のRubyは既に「ホットな言語」ではないとみなされているが、その分安定しているとも述べており、3×3がリリースされたときに再び「ホットな言語」として愛用されることを願っています。

成熟と停滞

Rubyは成熟のときを迎えています。自らを「成熟した」と言い切れるプログラミング言語はそう多くはありません。現状のRubyは、人気の高い言語の中では今も古株であり、実績も信頼もあるテクノロジーとして世界中で使われています。Rubyのコミュニティは絶え間なくRubyを支援しています。コードで何か困ったことがあれば、そして運良く同じ問題を数千人が踏んでいれば、かなり楽に解決方法を見つけられるでしょう。

残念ながら、成熟と停滞は表裏一体です。前述の通り、ここ数年はRubyを「ホットな言語」にする変更や新機能はそれほどありませんでした。Rubyもコミュニティももうお終いだという意見もあったりします。私個人は、Rubyのテクノロジーは停滞期を迎えていると考えています。率直に言うと、Rubyは極めてゆっくりとですが、レガシー言語になりつつあります。

少なくともここ数年、Rubyからときめきが失われていた。ただし、ときめきがないとだめということではない。長い目で見れば、プログラミング言語にとってときめきが必要だとも私は思わない。私としては、プログラミング言語には成熟と安定性と信頼性を求めたい。私は2019年のRubyについてそう考えている。私がRuby以外の言語に乗り換える必要のある分野は年々減り続けている。Rubyがこれまで「エンタープライズ向け」テクノロジーであったことはないし、今後そうなるかどうかも疑問ではあるが、高速かつ柔軟にやりたいことがあれば、Rubyは今後もよい選択のひとつである。
Filip Tepper(Castle Intelligenceの上級エンジニア)より

まとめ

皆さんが本記事を最後まで読んだとしても、Ruby on Railsを使うことで生産性が向上するとも思えず、アイデアとして非常に優れているとも思えないかもしれませんが、Rubyのエコシステムはまだまだやることをたくさん抱えています。Ruby on Railsのパワーを余すことなく活用すれば、洗練されたアプリをすぐにでも開発できるようになります。この機能については、他の多くのフレームワークでそこまで自信たっぷりに言い切ることはできません。なぜなら(はい、皆さんご一緒に)「欲しいgemは必ずある」からです。

ここ最近、そして近い将来のRuby on Railsのリリースはとても有望に思えます。開発者から寄せられた不満の多くが修正され、新バージョンが出るたびに素晴らしい新機能が追加されています。Ruby、そしてRailsが近い将来再び勢いを盛り返すことを願っています。


おたより発掘

関連記事

Railsは2018年も現役か?: 前編(翻訳)

週刊Railsウォッチ(20190204)あってうれしい40のgem、Ruby 2.6.1セキュリティリリース、Hanami v2.0.0.alpha1リリースほか

$
0
0

こんにちは、hachi8833です。試みとして、社内でのつっつくときの順序を変えて「Ruby」や「Rails」を後ろに回してみました。

「というわけで今回のつっつきエントリはクラウドから始まってますが、公開時にはいつもの順序に戻します」「お、いいんじゃないでしょうか: それなら途中からつっつきに参加する社員もRailsやRubyの話題をチェックできるし😊

  • 各記事冒頭には⚓でパーマリンクを置いてあります: 社内やTwitterでの議論などにどうぞ
  • 「つっつきボイス」はRailsウォッチ公開前ドラフトを社内有志でつっついたときの会話の再構成です👄
  • 毎月第一木曜日に「公開つっつき会」を開催しています: お気軽にご応募ください

⚓週刊Railsウォッチ「公開つっつき会#7」開催のお知らせ

次回の公開つっつき会は今週木曜日2/7に開催いたします。皆さまのご参加をお待ちしております🙇

⚓Rails: 先週の改修(Rails公式ニュースより)

今回の公式改修情報は日付時刻関連が多いようです。


つっつきボイス:「6.0.0のリリーススケジュールも決まったことだし、駆け込みで機能追加とかありそう」「Action TextAction Mailboxとかでしたっけ…要る?」「ちょ🤣」「🤣」「🤣」「まあまあ😆

「あ、パラレルテスト複数DBサポートが6で入るのはやっぱり大きい」「そのあたりは使う可能性高いっすね☺」「あと先週のZeitwerkローダーも」「新しいローダーか…入ってみないとわかんないし」「本当に入るんだっけ?」「RCには入れないけど最終的に入れるつもりではあるらしいですね」「微妙に怪しい😆

「Zeitwerkに変わった場合は今のコードを書き換えなくても動くらしいですけどね: もう君たちはrequire書かなくていいんだよみたいな勢いで」「RailsはZeitwerkでいけるとしても、Rails以外のシステムで果たして何が起きるか😆」「なまじ読み込み順が正しくなったために逆に動かなくなったり😆」「修正が前より楽になればいいけど」

⚓credential編集中に環境が読み込まれないようになった

# railties/lib/rails/command/actions.rb#L13
      def require_application_and_environment!
+       require_application!
+       require_environment!
+     end
+
+     def require_application!
        require ENGINE_PATH if defined?(ENGINE_PATH)

        if defined?(APP_PATH)
          require APP_PATH
+       end
+     end
+
+     def require_environment!
+       if defined?(APP_PATH)
          Rails.application.require_environment!
        end
      end

つっつきボイス:「そういえばmulti environment credentialとかあった: これってRails 6から?」「そのようです」「まあ自分はcredential yamlってどうよ派なので😆」「こないだ使ってたところありましたね」「credential yamlは、たとえばインフラエンジニア不在という状況でHerokuとかで使う分にはいいんだろうけど、まともなインフラエンジニアがいるなら、ちゃんとしたアプリに使うにはちょっとね〜: マスターパスワード抜かれた瞬間に全滅の危機が訪れるし☠」「しかも(暗号化されてるとはいえ)コミットに含まれてますし」「引き返しようがなくなることを考えるとコワい😨

「credential yamlのencに入れてもよさそうなものがあるとすれば、簡単に無効化できる、無効化されてもいいような情報かな」「そっか〜確かに」「簡単に無効化できないものを入れてしまうと永遠の脆弱性になりかねないし😭

「secret key baseを変えないといけないとなると、(もしcookieに署名する設定使ってたら)cookieがほぼほぼ全滅してユーザー全員ログアウトとかになりそうだし」「cookie全滅したら普通に全員ログアウトしますね🧐」「secret key baseの変更はサービスによっては影響がでかい可能性ある: サーバー負荷が上がるとか」「ユーザーのパスワードも影響される?」「パスワードにはkey baseは使われないから大丈夫」「あ、パスワードはsaltとかそっちの方か」

参考: ソルト(salt)とは | セキュリティ用語解説 | 日立ソリューションズの情報セキュリティブログ

なお、cookies.signedを使わないとcookieに署名はされないとのことでした↓。

参考: ruby-on-rails session 有効期限 - Railsの署名付きCookieと暗号化Cookieの違いは何ですか? - CODE Q&A 問題解決

「ロードバランサー経由で片方のサーバーだけにデプロイしたりすると、ログインしたユーザーが画面をリロードしたらログアウトしたなんてことが起きたりとか」「そしたら問い合わせ殺到😅」「考えるだけで胃が痛くなるし」「ではお次へ〜」

⚓巨大な値があっても正しい値を返すよう修正

kamipoさんの2年前のキリ番がマージされました。


rubyonrails.orgより

修正前は以下がfalseになるそうです。

# 同PRより
assert_equal true, Topic.where(id: [1, 9223372036854775808]).exists?
assert_equal true, Topic.where.not(id: 9223372036854775808).exists?

つっつきボイス:「2年越しのPRですが、キリ番が一番注目を浴びてるような」「ああこのキリ番以前見たし☺: Topic.where(id: 1..9223372036854775808).find(1)みたいな値をぶちこまれたらビビる😆」「😆

⚓時刻パラメータを複数含むハッシュをキャストすると年が正しくなかった問題を修正

# 同PRより
# 修正前
event = Event.new(start_time: { 4 => 20, 5 => 30 })
event.start_time # => 1970-01-01 20:00:00 UTC
event.save
event.reload
event.start_time # => 2000-01-01 20:00:00 UTC

# 修正後
event = Event.new(start_time: { 4 => 20, 5 => 30 })
event.start_time # => 2000-01-01 20:00:00 UTC
event.save
event.reload
event.start_time # => 2000-01-01 20:00:00 UTC

つっつきボイス:「この{ 4 => 20, 5 => 30 }って何この書き方〜!!!🥶」「え〜、ハッシュのキーが生の数値って😆」「何それコワい」「4番目が20で5番目が30…?知らずに見たらまったくわからん😆」「キーの数値がhhとかmmとかを指してるっぽい🤔」「普通使わないな〜」「cronの使い方並に覚えられない😆」「time hashってこれのことなんだろうな」

「やっぱりこの書き方ヘンですよね?」「Rubyでこんな書き方知らんけど😆、ヘンということはないです: たぶんこれはUnixの伝統的なdate関数あたりが由来で、引数の順序までPOSIXあたりで決まってたと思うので、たぶんその数字じゃないかな」「あ〜そっちですか!」「上のインターフェイスもPOSIXでの定義だと思われるので、それに合わせること自体は不思議ではない」「順序の数字そのものズバリのドキュメントがうまく見つからない…😢」「man dateにもなさそう」

参考: date - コマンド (プログラム) の説明 - Linux コマンド集 一覧表

「コメントでは若手から先生にこんな質問もあがってました↓」「へ〜」


同PRより

「POSIXのデフォルトの日付って1970年から始まってましたね」「いわゆるUNIX時間でしたっけ」「UNIX元年😆」「そういえばPHPだと引数なしの場合に現在の日時が取れたな: 引数なしの挙動はライブラリとか言語で違ってた気がする」「そうだったかも」

参考: UNIX時間 - Wikipedia
参考: 2038年問題 - Wikipedia

「知らずにコードレビューで{ 4 => 20, 5 => 30 }とか見たらきっとビビる😆: 動くのこれ?みたいな」「気持ちはわからんでもないけど」「せめてキーの部分は何らかの定数的なものにして欲しいし」「ま、そんなにしてまで使う機能じゃないし😆

⚓Date#advanceのメモリ効率を改善

# activesupport/lib/active_support/core_ext/date/calculations.rb#L112
  def advance(options)
-   options = options.dup
    d = self
-   d = d >> options.delete(:years) * 12 if options[:years]
-   d = d >> options.delete(:months)     if options[:months]
-   d = d +  options.delete(:weeks) * 7  if options[:weeks]
-   d = d +  options.delete(:days)       if options[:days]
+
+   d = d >> options[:years] * 12 if options[:years]
+   d = d >> options[:months] if options[:months]
+   d = d + options[:weeks] * 7 if options[:weeks]
+   d = d + options[:days] if options[:days]
+
    d
  end

つっつきボイス:「おーDate#advanceが速くなった😍」「これってよく使われるんでしょうか?」「そりゃもうめっちゃ使われてるし!: 間接的な呼び出しを含めれば使ったことない人はいないハズ😎」「直接呼ばないにしても、tomorrowみたいな日付がらみのヘルパー呼んでれば内部的にはここを最終的に通ることも多いはずだし」「今さらですが、advanceは未来の日付を指すんですね」

「ビットシフト>>使ってる?」「ビット演算なら速そう😍」「高速化はoptionsでhash#deleteを使わないようにしたことで行ったっぽいですね: あと>>はもしかするとDateのメソッド?(わかんないけど)」「Date#advanceが速くなればRailsアプリ全体も速くなるはず👍

参考: class Integer (Ruby 2.6.0)

⚓Action Viewのview_pathsの改変を避ける

先週のウォッチで取り上げた#35031に続いてAction Viewが改修されていました。

# actionview/lib/action_view/lookup_context.rb#L134
      def with_fallbacks
-       added_resolvers = 0
-       self.class.fallbacks.each do |resolver|
-         next if view_paths.include?(resolver)
-         view_paths.push(resolver)
-         added_resolvers += 1
-       end
+       view_paths = @view_paths
+       @view_paths = build_view_paths((view_paths.paths + self.class.fallbacks).uniq)
        yield
      ensure
-       added_resolvers.times { view_paths.pop }
+       @view_paths = view_paths
      end

    private

+     def build_view_paths(paths)
+       ActionView::PathSet.new(Array(paths))
+     end

つっつきボイス:「今週もAction Viewに@tenderloveさんが手を加えてました」「view_pathsを上書きしないようにしたのね: たしかに処理の途中で改変されたらそれはそれは悲惨なことに😆」「リードオンリーになるべきものがなってくれるのはいい改良👍」「tenderloveさんは前のコードが読みづらいから修正したみたいなことを先週書いてましたね」「書き換えが発生するものは引数で渡す(それ以外のコンテキストについては書き換えない)というのが本来のあり方ですし」「ですね☺

⚓番外: ActionCableでRedisとの接続が切れた後再接続しない

2017年1月以来オープンされたままのissueですが、社内で踏んだ人がいると小耳に挟んだので。


つっつきボイス:「ああこれね😆: この中にひとり、これを踏んだ人が😎」「踏んだのはボクじゃなくてこっちの人😆」「😆」「ボクはRedis立ち上げっぱなしだったので無罪」「ワイDocker環境じゃなかったから踏んだ😇

「それにしてもこのissueをピンポイントでよく見つけましたね」「やーそれはホントびっくり‼」「一番下のバックトレースでググったら出てきました☺」「なるほどね〜」

「しかしこのissue2017年か😅」「ラベルをpinnedってするとボットがcloseしなくなるっぽいですね↓」「へ〜」「未対応なのにcloseされてないからそうかなと思って😆」「これは+1👍押しといた方がいいヤツ?」「自分も押そうっと」

同issueより

「つことは、このまま待っててもresolveしないってこと?😇」「運用でカバーできるといえばできるけど」「プルリク投げるなら英文代筆しますよ🖋」「おそらくこの修正は簡単にはいかないでしょうね: 真面目にconnectionチェックまでやろうとすると地味にめんどいし」

今通っている歯医者で小さな虫歯を発見されたときに「これはまあ様子見ですね」と言われたのを思い出しました🦷

⚓Rails

⚓あってうれしい40のgem


同記事より


つっつきボイス:「今までいろんなgemを追いかけたり教えてもらったおかげか、ほとんどのgemが見覚えありました: ちょっとgem多すぎかなとも思いましたが」「どれどれ👀

「Rubocopは定番として、overcommitって前にも話題になった気がしました(ウォッチ20180302)」「overcommitはフックでgit pushとかしてるだけだし😆」「強制されるとウザくなりそう😆」「これ何かの案件に入ってたような?」「それはきっとNode版のovercommitのhusky↓ってヤツじゃ?」「それそれ!コミットできなくて1日悩んだし😭

// typicode/huskyより
// package.json
{
  "husky": {
    "hooks": {
      "pre-commit": "npm test",
      "pre-push": "npm test",
      "...": "..."
    }
  }
}

「huskyってコマンドでGit使わないと動かないとかあります?」「いえ、Sourcetreeでコミット足止め食らったのでGUIでも大丈夫かと」「ローカルのGitリポジトリに対してかけられるなら大抵の環境で大丈夫そうかな😋」「フックでやらせる処理が軽ければいいけど、重かったらイヤだな🐘: RSpecとか」「コミットメッセージを書き直しただけでフルテストが走ったりしたらつらい😇」「😆」「😆

「better_errorsは結構好き🥰」「私も😍」「ああ下にコンソールが出るヤツ」「定番だと思ってた」「使ったことなかった気がする: RubyMineのブレークポイントばっかり使ってたので💎 」「お?それはもったいない: 特にビューの途中で落ちたときにブラウザでその瞬間をコンテキストごと捉えてそのままアタッチできるのでありがたい🙏」「開いたまま時間が経つとタイムアウトしちゃいますけど⏱」「バックトレースを駆け上がることもできましたっけ?さすがに無理かな?」「バックトレースはどうだったかな…ただその瞬間のビューのコンテキストを取れるのは間違いない」「そこから辿ることはできそうですね😋」「ビューのデバッグのしやすさでbetter_errorsは好き」「あーこの画面ですか↓: 使った使った😘


BetterErrors/better_errorsより

「byebugってたどり着くのが案外面倒でたまに諦めたことありますし😆」「byebugそんなに好きじゃない😆」「自分で立ち上げるときはbyebug普通に打ってましたけど、Docker環境になるとdocker-composeの設定にあれやこれやを入れないとbyebugが動かないんで諦めてデバッグプリントに走りました🤣」「🤣

「Capybara Screenshotって、今はRails単体でできた気がするんですけど」「そういえば」「真ん中あたりのgemに古めのものありますね: database_cleanerとか」「database_cleanerは古いな」「テストを速くするためだけに入れる可能性は今もあったりするかも: ロールバックよりtruncateの方が速いみたいな」「ほむほむ」「shoulda-matchersとか使う?オレらアサーションでいい派だし😆

「rspec-retryって初めて見た」「何回かに1回しか落ちないテストを、落ちるまで回すみたいな?」「鼻血が出るまで殴るみたいな😆」「この間そのためにfor文で100回回しましたよ🤣」「🤣」「Docker-composeだとテストのたびに環境がキレイになっちゃって再現できないので😅」「あーわかりみ: 同じコンテナを使い回す形でやらないといけないヤツ」「rspec-retryがそこまでやってくれるなら、ちょっとうれしいかも☺

「Fakerは日本語の語彙が未だに少ないのが悲しい😅: みんなもっと足そうよ」

「このminaって?」「へー、Capistranoのオルタナティブ的なデプロイツール」「こんなのあるのか」「★4000近いですね」「正直デプロイツールは何でもいいし😆」「どれも似たようなものですし😆」「デプロイツールに求められるものって実はそんなに多くないし」「メンテされてれば何でもいいのっ😊


同リポジトリより

# 同リポジトリより
$ mina deploy
-----> Deploying to 2012-06-12-040248
       ...
       Lots of things happening...
       ...
-----> Done.

「割と古めのgemもところどころにあるし😆」「長年動いている案件でgemが降り積もってるとか😆」「ここにあるのも含めてadmin系gemはどうも好きじゃない😕

「お、simple_formはどう?: キライな人もいるけど自分はそんなにキライじゃない🥰」「simple_formは使い所によるかなー☺」「カスタマイズしないと固く誓うなら😆」「それな: カスタマイズした途端に容易に地獄化する👹」「同意〜」「なにしろsimple_formが生成するデフォルトのBootstrapテンプレートがほぼカスタマイズ不可能だから😅

「dotenvは(・∀・)イイ!!」「これはいいヤツ😍」「Railsにデフォルトで入ってもいいぐらい」「うんうん」

「pg_search知らないな〜」「PostgreSQLで全文検索できるんですって」

「翻訳記事↓も出したことあるschneemsさんのwickedってgem、名前がスゴイ😆」「『邪悪な』」「お、これはRailsでウィザード的なstep by stepの画面遷移がやれるのか!」「Windowsのインストーラみたいなヤツですよね: こういうのってRailsでやるのが大変って聞いたことあります」「そうそう😤: 超めんどくさいっ」「wizardだからwickedってシャレなのかも」


同リポジトリより

データベースのランダム読み出しは要注意(翻訳)

「wickedはコントローラでこうやってstepsで遷移を定義できるのか↓」「ほほぉ〜」「いわゆるウィザード形式のUIって、リクエストがどうしてもRails wayに乗ってくれないのがつらいんですよね」「ほんとそう!😭: アクションとか大変すぎ」「この間ハマってましたね」「それならいっそこういうgemに任せるのも悪くないのかも?」「wicked、あんまり更新されてないっぽいのがちょと引っかかるけど😆」「今ならJavaScriptでやろうぜみたいな風潮ですし😆」「今だとJSでやるのがいいのかな〜🤔

参考: ウィザード (ソフトウェア) - Wikipedia

# 同リポジトリより
class AfterSignupController < ApplicationController
  include Wicked::Wizard

  steps :confirm_password, :confirm_profile, :find_friends

  def show
    @user = current_user
    case step
    when :find_friends
      @friends = @user.find_friends
    end
    render_wizard
  end
end

Wickedで、レイ・ブラッドベリの名作「何かが道をやってくる」の原題↓を思い出しちゃいました。

参考: Something Wicked This Way Comes (novel) - Wikipedia
参考: レイ・ブラッドベリ - Wikipedia

「money-railsは前に翻訳した記事↓にありました」「impressionistって印象派のアレ?と思ったらSEO用のgemでした」「PVカウンタというかリクエストロガー的な」「データベースがめちゃ重くなりそうな匂いが😆


charlotte-ruby/impressionistより

参考: 印象派 - Wikipedia

Ruby on Railsで使ってうれしい19のgem(翻訳)

「オーラスはroute_translator」「これは?」「ルーティングを辞書形式に変換してくれるみたい」「おー😳

es:
  routes:
    admin: administrador
    cars: coches
    new: nuevo
    pricing: precios
    sold: vendidos
fr:
  routes:
    admin: administrateur
    cars: voitures
    new: nouveau
    pricing: prix
    sold: vendues

「残り時間少なくなってきたので次へ〜」

⚓Ovtoフレームワーク


同サイトより

詳しくはるびま記事をどうぞ🙇


つっつきボイス:「この後のhyperappから影響を受けたのがこのovtoだそうです」「ほほぉ〜」「まだちゃんと読んでませんが、どうやらOpal↓でRubyをJavaScriptに変換してるっぽい」「んーOpalか🤔」「やってみる分にはいいかも☺


opalrb.comより

⚓その他Rails


つっつきボイス:「お、価格改定か: この間買ったところだし💰


参考: 生ハム原木を買ってはいけない(2016年追記あり) | N-Styles


つっつきボイス:「この釣り記事もワラタ😆」「サムライズムさんがツイートしてることからも察しですね☺

⚓Ruby

⚓run.rb: RubyをWebAssemblyとブラウザで実行(Ruby Weeklyより)

run.rbは「ランナービー」と読んで欲しいそうです。


つっつきボイス:「サイトでRubyがインタラクティブに動きます」「そういえばこれも何かで見た!」「mrubyじゃなくてRubyでWebAssemblyしてるのが凄そう」「後はどこまでちゃんと動くか😆

mrubyをWebAssemblyで動かす(翻訳)

⚓Ruby Toolboxで多くのバグが修正(Ruby Weeklyより)


ruby-toolbox.comより


つっつきボイス:「Ruby Toolboxって運営が行き詰まった後に再開したんでしたっけ↓」「そうそう復帰してた」「しかし結構基本的なバグ修正ですね」「もっと広告とか置いて稼げばいいのに💵

週刊Railsウォッチ(20180216)Rails 5.1.5リリース、DHHのYouTubeチャンネルは必見、Ruby Toolboxが運営再開ほか

⚓RubyからOAuthでGoogleサイトにアクセスするコード書いた(Ruby Weeklyより)

# 同記事より
class GoogleCredentials
  def load_user_refresh_credentials
    @credentials = Google::Auth::UserRefreshCredentials.new(
      client_id: @client_id,
      scope: @scopes,
      client_secret: @client_secret,
      refresh_token: refresh_token,
      additional_parameters: { "access_type" => "offline" })
    @credentials.fetch_access_token!
    return @credentials
  end
  def refresh_token
    @refresh_token ||= `security find-generic-password -wa #{@refresh_key}`.chomp
    @refresh_token
  end

Martin Fowlerさんがこういう記事を書いたのを初めて見たような気がします。


つっつきボイス:「記事そのものより、あのMartin Fowlerさんが書いてるという方が珍しそうだったので」「たまには普通のことだってやってるんだぜと言ってみたいとか😆」「まさにやってみた系記事そのまんまじゃないですか😆」「たまにはアプリケーションコード書いてみたいだろうし」「設計ばっかりやってるわけじゃないぞみたいな😊」「Martinさんが書くから出せる面白み☺

参考: マーティン・ファウラー - Wikipedia

⚓Rubyの!?ネーミングとその由来


つっつきボイス:「Rubyメソッド名の!?と、その歴史を解説してる記事です」「そうそう、RubyはPerlとかSmalltalkとかEiffelとかAdaとかLispの記法を参考にしてるとある」「Matzの好きな言語☺」「Eiffelってエッフェル塔と同じスペルでしたね🇫🇷: 英語風だとエイフェルって感じの発音」

参考: Eiffel - Wikipedia
参考: エッフェル塔 - Wikipedia

「そしてラストに日本風の顔文字が↓」「相当昔の2ちゃんっぽいし😆」「しかも明朝体😆」「この初々しさがたまらん👶


同記事より

⚓Hanami v2.0.0.alpha1がリリース(RubyFlowより)


hanamirb.orgより

次のアルファ版は今年4月🌸、最終版はその後今年のどこかで出すようです。

  • 旧Lotus時代の技術的負債をクリーンアップするため、スクラッチから作り直す
    • 幸い1.3のコードは4000行程度
    • 1.3との互換よりも2のプレビューという位置づけ
  • 設定ファイルをconfig/application.rbに集約
  • 設定ファイルのDSL構文を改良
cookies max_age: 600              # 旧
config.cookies = { max_age: 600 } # 新
  • 設定ファイルですべてを明示的に書く方針から、デフォルト設定を使う方針に変更
# apps/web/application.rb
security.x_frame_options = "DENY" # 2.0.0ではデフォルト値なので生成されない
  • ルーティング(hanami-router)をmustermannベースに変更した
    • リポジトリ: sinatra/mustermann
    • 複数のHanamiアプリを1つのルーター(ルーティングファイル)でまかなえるようになった
  • アクションクラスが書き直された
    • 従来はHTTPリクエストごとにアクションのインスタンスが生成されてGCに負担をかけていたのを改めた
    • アクションがイミュータブルになり、expose DSLが不要になった
    • Hanami::Actionを継承するようになった
# frozen_string_literal: true

module Web
  class Action < Hanami::Action
  end
end
  • コードの再読み込み機能をhanami-reloaderに切り出した
  • 2.0のCLIコマンドは作り直し中(今はhanami versionhanami serverのみ)

つっつきボイス:「Hanamiをがっつり書き直しているらしいです」「へ〜」「まあHanamiを今使ってる人たちはRailsが好きじゃない人たちだろうし☺」「やっぱりHanamiだけに4月に何かリリースしたい感じなんでしょうね」

「LotusっていうのがHanamiの元々の名前だったそうですが、負の遺産を払拭するために互換性も振り捨てて取り組んでるみたい」「今も4000行程度なら、Hanamiがこれを機会にJetsみたいなうんと軽量化した方向に進めば逆にサーバーレスの分野を取りにいけるかも?🤔」「それは見込みありそうですね🥰」「HanamiってRack使ってると思ったけど、Rackベースで動くならRailsでいいんじゃね?って思ったりするし」

↓後で調べると、HanamiはRack使ってますね。

参考: Search · Rack

「今までのHanamiはリクエストごとにアクションのインスタンスを生成したのを変えたりしてますね」「まあリソースは抑えないとパフォーマンス上げづらいし」「改良していくうちにRailsみたいになってきたりして😆」「😆

⚓その他Ruby


つっつきボイス:「そんなに長くはない記事ですが」「そうそう、digを掘るときにundefinedになるのはよくあるし」「あとはprotectedキーワード」「確かにRubyのprotectedは難しい😭」「使ってる人いるんでしょうか?」「きっとわからないから使われないんじゃ?😆」「わかるけど、わかってないこともいっぱいありそうな気がして😅」「使いこなせる気がしない🤪」「それからOpenStruct」「あれね☺: Structは使うけど」

# 同記事より
user_info[:family_members][:grand_father][:name]
# => `NoMethodError: undefined method `[]' for nil:NilClass`

つっつきボイス:「スクリプトエンジンということだけど、どうやらまだ作り終わってないっぽい😆」「こんな感じ↓で最後にScriptEngine.evalでちょい無茶っぽく動かしてる」「時間ないので次へ〜」

module ScriptEngine
  class << self
    def engine
      @engine ||= ScriptCore::Engine.new Rails.root.join("mruby/bin")
    end

    def eval(string, input: nil, instruction_quota_start: nil, environment_variables: {})
      sources = [
        ["user", string],
      ]

      engine.eval sources, input: input,
                  instruction_quota_start: instruction_quota_start,
                  environment_variables: environment_variables
    end
  end
end

ScriptEngine.eval "@output = 'hello world'"


⚓Ruby trunkより

⚓Ruby 2.6.1セキュリティリリースとissue(Ruby公式ニュースより)


つっつきボイス:「お、2.6.1がリリース: この間つっつきで話した、2.6で挙動が変わる云々の修正(ウォッチ20190107)とかではなく?」「かなと思ったんですがセキュリティの修正のようでした」

⚓stepでゼロを指定したい

# 同リポジトリより
>> 1.step(10, by: 0) { break }
=> nil
>> 1.step(10, 0) { break }
Traceback (most recent call last):
        5: from /Users/mrkn/.rbenv/versions/2.6.0/bin/irb:23:in `<main>'
        4: from /Users/mrkn/.rbenv/versions/2.6.0/bin/irb:23:in `load'
        3: from /Users/mrkn/.rbenv/versions/2.6.0/lib/ruby/gems/2.6.0/gems/irb-1.0.0/exe/irb:11:in `<top (required)>'
        2: from (irb):5
        1: from (irb):5:in `step'
ArgumentError (step can't be 0)

つっつきボイス:「stepでゼロを許したいそうです」「あーなるほどわかりみ」「ゼロを使いたいことってあるんでしょうか?」「変数だったらゼロが入る可能性があるので」「あ、そうか😅」「ステップがゼロのときに例外を吐いて欲しくないということでしょうね」「ゼロなら何もせずにスルーしてくれと」

「この辺は言語の設計思想にもよりそう: たとえばSmalltalkなら明らかにおかしなものを渡されない限りは許容するみたいなところがありますし」「ほむほむ」「Javaの世界ならきっちりしないといけないみたいな」

参考: Smalltalk - Wikipedia

⚓クラウド/コンテナ/インフラ/Linux/Serverless

⚓マイクロソフトがクラウド関連のソースコードを公開


つっつきボイス:「マイクロソフトが業務システムのソースを公開だそうです」「Azure前提ですけどね☺: ははぁ、こういう感じのシステム構成↑をパッケージとして公開するということかな」

「今さらですが、日本ではこういうふうに『パッケージに合わせて業務を組み直す』という形のシステム導入が苦手というかさっぱり定着しないんですよね😅」「たしかに英語圏とかと比べるとそれを感じますね: 以前銀行システム経験者から聞いた話なんですが、銀行システムってパッケージ化されたものが実は前からちゃんとあって、それに合わせて業務を再編成する形で導入すれば圧倒的に低コストだし安定するのにって」「そうそう、そういうパッケージはずっと前から普通にあるけど、日本では現行の業務に合わせてカスタマイズしないと使ってくれない😆」「『こうじゃないと使いたくない』みたいな😆」「受託案件はほぼそういうものですし、パッケージをそのまま導入するケースは皆無☺

後でググってみると、ガートナーが調べた勘定系システムパッケージのランキング↓がありました。SAPとかOracleもエントリーしてますね。

参考: Global Retail Core Banking Systems Reviews

「英語圏でもクローズドな受託開発は当然いっぱいありますよね?」「普通にありますね🤓」「普段そういう情報をあまり見かけない気がするのは、自分が普段チェックしているのがオープンソース関連ばかりだからなんだろうなと思いました😅」「さもありなん☺

⚓AWS CloudWatchとは

チームミーティングで話題になったので。


つっつきボイス:「CloudWatchを手短に言うとどんな感じでしょう?」「Munin的なモニタリング・監視ツールですね: すごく昔からあるし、何の変哲もないというか新しいことは何もない😎」「おー」

「ついでに見つけたのが、CloudWatchにちょっと追加があったという記事です」「CloudWatchに最近更新とかあったっけ?🤔: なーんだ、CloudWatch Agentでディスク使用量やメモリ使用率が取れるという話か」

「AWSでは、仮想マシン内のディスク使用量やメモリ使用率って基本的に取れないんですよ」「というと?」「ちょうど今日の社内勉強会でも話したんですが、AWSには責任共有モデル(Shared Responsibility Model)というものがあって、OSより上の部分(この図↓で言う緑の中にある)はユーザーに属しているので、AWSのサービスであるCloudWatchからはそういう部分に関与することは本来ない」「そういえばそうだった😳」「もちろん、たとえば物理メモリをどのぐらい割り当てるみたいな情報はAWS側にありますが、その物理メモリが仮想マシンでどう使われているかといった情報をCloudWatchから取りに行くことはデフォルトではやらない」「なるほど!」「そういう責任範囲の境界線を定義しているのが責任共有モデルです🧐

参考: 責任共有モデル – アマゾン ウェブ サービス (AWS)


aws.amazon.comより

「2つ目の記事のCloudWatch Agentの話も、ユーザーがAgentをインストールすればカスタムメトリクスとして代表的な項目をいくつか指定できる、それを公式ツールとして出したというだけのことで、特に目新しさはない: 今までもユーザーが同じことを手動でやれていたので🧐」「そうだったんですね😃」「まあAWSを使ったことがないとCloudWatchはよくわからないと思いますが」

「ただ、CloudWatchそのものはあまり高機能ではないという点には注意が必要」「勉強会でもその話がありましたね」「CloudWatchの機能を超えるようなことをさせるのはあまりよくなくて、むしろCloudWatchの守備範囲に合わせてメトリクスとかを構築する方がよいと思います☺」「たとえばNagiosならNRPEというプラグインを使えば所定のスクリプトを自由に実行できるようになる」「Nagios Remote Plugin Executerって何だかスゴイ名前😆


nagios.orgより

参考: NRPEを使った監視を実装する - think-t の晴耕雨読

「NRPEだと以下のようにcheck_なんちゃらのように指定する」「おー」「なのでNagiosなら何でもできるという利点はありますね😎

# think-t.hatenablog.comより
# /usr/local/nagios/libexec/check_nrpe -H 127.0.0.1 -c check_users -a 5 10
USERS OK - 1 users currently logged in |users=1;5;10;0

# /usr/local/nagios/libexec/check_nrpe -H 127.0.0.1 -c check_load -a 5,5,5 10,10,10
OK - load average: 0.03, 0.01, 0.04|load1=0.030;5.000;10.000;0; load5=0.010;5.000;10.000;0; load15=0.040;5.000;10.000;0; 

# /usr/local/nagios/libexec/check_nrpe -H 127.0.0.1 -c check_procs -a 1:1 1:1 root auditd
PROCS OK: 1 process with UID = 0 (root), command name 'auditd'

# /usr/local/nagios/libexec/check_nrpe -H 127.0.0.1 -c check_cpu -a 80 90
OK - user: 8.83, nice: 0.50, sys: 16.40, iowait: 0.50, irq: 0.50, softirq: 0.50 idle: 76.25 | 'user'=8.83 'nice'=0.50 'sys'=16.40 'softirq'=0.50 'iowait'=0.50 'irq'=0.50 'idle'=76.25

# /usr/local/nagios/libexec/check_nrpe -H 127.0.0.1 -c check_mem -a 80 90
OK - 16.0% (80268 kB) used.|TOTAL=502720KB;;;; USED=80268KB;;;; FREE=422452KB;;;; CACHES=46952KB;;;;

# /usr/local/nagios/libexec/check_nrpe -H 127.0.0.1 -c check_swap -a 20% 10%
SWAP OK - 100% free (991 MB out of 991 MB) |swap=991MB;198;99;0;991

「CloudWatchではあまり複雑なことはしない方がいいんですね」「CloudWatchに限らず、AWSは単機能を組み合わせる形で複雑なものを構築するようになっています」

「CloudWatchの方はMuninとかNagiosのサーバーを立てなくて済むという利点があるので、知らないよりは知っておく方がいいですね☺」「詳しい説明ありがとうございます!」

⚓SQL

⚓MicrosoftがCitus DataをM&A(DB Weeklyより)

PublicKeyでも報じられていました。

参考: マイクロソフト、PostgreSQL用の分散DBエンジンをオープンソースで開発するCitus Dataを買収。Azure上のPostgreSQLサービスを強化 - Publickey


つっつきボイス:「最初見たときMicrosoftがPostgreSQLを買収したのかと見間違えてしまいました😅」「postgresql-jpのSlack↓でもこのニュースが話題になってましたね: Azureでぽすぐれが使いやすくなるなら良い話☺

「Citus DataはPostgreSQLのクラスタをやってるらしい」「TechRachoでもCitus Data社の技術記事↓を何度か翻訳したことがあって、PostgreSQLクラスタに強いという触れ込みでした」「クラスタ機能はPostgreSQL自体にあるんですが、復旧とかパフォーマンス・チューニングなんかを本格的にやろうとするとかなり大変😓

Rails開発者のためのPostgreSQLの便利技(翻訳)

「ま、そこまで壮大なものを求められる案件はめったにないけどっ😆: それほどの規模になるということはシステムの重要度がとても高いということで、問題が発生したときにオープンソースのデータベースをエンジニアがちょっと直してどうにかなるようなレベルを超えてしまうので、安心のためにもサポートのしっかりしたエンタープライズ版を購入するという流れになりますね」「Oracleとか?」「Oracleはクローズドで購入以外の選択肢がないから、Citusとはちょっと違うかな」「あっそうか💦」「Perconaが出しているカスタマイズMySQLの有償サポートとかの方がCitusの位置付けに近いかも」「あとPassengerのWebサーバーのEnterprise Editionとかですね」「Redisもそうなったりして😎」「くわっ😰


percona.comより


phusionpassenger.comより

⚓PostgreSQLバックエンドのフローチャート(Postgres Weeklyより)


postgresql.orgより


つっつきボイス:「公式サイトのチャートです」「へー、これって最新のぽすぐれを反映しているんだろうか?🤔基本的な部分が変わってることがもしかするとあるかもしれないし」「そういえば」「きっと今積読中のぽすぐれ本に書いてあるに違いない😅

後でarchive.orgで見てみると、フローチャートは2012年から変わってないようです↓。

archive.org: PostgreSQL: Backend Flowchart

⚓NVMeでキャッシュがどれだけ速くなるか(DB Weeklyより)


つっつきボイス:「ちょっと長い記事なんですが、データベースとはあまり関係なさそうでした」「キャッシュと言えばPostgreSQLなんかだとRAMどころかL1〜L3キャッシュに乗せるなんてこともしますけどね: ざっと眺めたところでは、NVMeに乗せるとすごく速くなるよみたいな話っぽい」「NVMeが何なのかまだわかってませんでした😅」「NVMeは確かに物凄く速い🚀: たとえば800メガバイト/secとか出ますよ」「そんなに!😳」「最近のSSDの接続規格はたいていNVMe🧐: 体感で4倍ぐらいは違う」「強い💪

参考: NVM Express - Wikipedia

「M.2のスロットに挿すことが多いんですけど、M.2自体ははSSDとNVMeのどちらでも挿せるようになってて、NVMeの方が圧倒的に速い」「知らなかった〜」「今使ってるWindows機では実行速度で800MB/sec出ましたからね(修理中だけど😭)」「やべ〜」

参考: M.2 - Wikipedia

参考: 価格.com - NVMeのSSD 人気売れ筋ランキング

「そこまで高速ならハイバネーションもめちゃ速い: 1分かからないし」「あー、この間SlackのWindows板で、メモリをむやみに増やすとハイバネーション激遅になるよみたいな話が出てましたね」「それそれ: これが200MB/secぐらいだと5秒で1ギガぐらいしか出ないので」

「もちろんNVMeだけじゃなくて、トライステート(ものによっては4ステート)の素子を使ったりしているものだとさらに速い」「トライステート、もう実用になってるのか〜」

参考: スリーステート・バッファ: デジタル回路 - Wikipedia

「それもあってSSDはものによって速度がまるで違うし: 書き換え回数なんかも1000MB/sec出るものだと4倍ぐらい差が出るし、そういうのはランダムアクセスも速いし」「お〜」「1Gバイト/secはなかなか壮観ですネ😋」「ビットじゃなくてバイト!お値段も張りそうですけど」「そうでもないし: 自分が買ったSSDは2TBで3万円台ぐらい」「それなら手が届きそう😋」「もちろん速度重視か容量重視かとかで値段はかなり変わりますが」「ノートPCでも使えます?」「もう今のノートはたいていこれです😎: M.2スロットでNVMeは普通」「そうでしたか💦

「記事長いからろくに読んでないけど、今ならデータベースのキャッシュをRAMで頑張るよりNVMeとSSDで頑張る方がいいんじゃないのみたいな感じかなと推測: ランダムアクセスも速いんだし」「そんな感じっぽいですね」「ストレージの速度が桁単位で向上してきたら当然戦略も変わってきますし: 従来はRAMとディスクの速度差はものすごく大きかったし」「そういえば以前の勉強会で、RAMとディスクはざっくり1000倍は違うって話がありましたね」「昔はキャッシュアウト=死みたいな感じでしたが、ディスクがこれだけ速くなってくると差はかなり縮まってくるし: まあRAMの方も速くなってますが☺」「記事はやっぱりデータベースとはあまり関係なさそうですが、読んだら面白そう😃

⚓その他SQL

-- 同記事より
SELECT client_addr,
       pg_wal_lsn_diff(
           pg_current_wal_lsn(),
           sent_lsn
       ) AS sent_lag,
       pg_wal_lsn_diff(
           pg_current_wal_lsn(),
           write_lsn
       ) AS write_lag,
       pg_wal_lsn_diff(
           pg_current_wal_lsn(),
           flush_lsn
       ) AS flush_lag,
       pg_wal_lsn_diff(
           pg_current_wal_lsn(),
           replay_lsn
       ) AS replay_lag
  FROM pg_stat_replication;

つっつきボイス:「explainで戦うとかそういう感じかなと思いつつ、pg_wal_lsn_diff?」「WALって何でしたっけ…」「つ↓」「Write Ahead Logging!」「それそれ: たしか2相コミットの中のどっちかで行われていたような覚えが」

公式: 30.2. ログ先行書き込み(WAL)
公式: 30.3. 非同期コミット
参考: PostgreSQL×PHPの2相コミット: なんちゃってプログラマのぼやき

「こんな感じでPostgreSQLのアーキテクチャのどこで詰まっているかを解明するみたいなところまでパフォーマンスを追い込んでいくと、ぽすぐれエンジニア感ある💪」「強そう🤼‍♂️

⚓JavaScript

⚓hyperapp: サイズ1kBのマイクロフレームワーク


つっつきボイス:「hyperapp、どっかで見たような🤔」「この間のるびまで特集されてたOvtoはこれに刺激されて作ったそうです」「AWS Lambdaに乗せるならこのぐらい小さくていいんじゃねみたいな感じなんでしょうね」「★16,000超えってめちゃ多い😳」「Incrementsの人が作ってたか使ってたような覚えが」

参考: 新QiitaでReactをやめてhyperappを採用した背景 - Qiita

「ふ〜む、ちょっとパーシャル返すぐらいならこのぐらいのコード↓で十分な気もしてくる」「サーバーレスが流行りだしてからこういう小ぶりなフレームワークが増えてきた感じですね: 今に限らないんでしょうけど」「サーバーレスでプロセスコンテナ的にやるなら、ライブラリは小さければ小さいほど起動が速いので有利ですし」「ですね: CGIのアナロジーでしたっけ」

// 同記事より
import { h, app } from "hyperapp"

const state = {
  count: 0
}

const actions = {
  down: value => state => ({ count: state.count - value }),
  up: value => state => ({ count: state.count + value })
}

const view = (state, actions) => (
  <div>
    <h1>{state.count}</h1>
    <button onclick={() => actions.down(1)}>-</button>
    <button onclick={() => actions.up(1)}>+</button>
  </div>
)

app(state, actions, view, document.body)

参考: Common Gateway Interface - Wikipedia
参考: lambdaのノート、Ruby編 - @m_seki の

「お、lazy componentなんてのもあるし↓、必要最低限のものはだいたい揃ってそうかな」

import { h } from "hyperapp"

export const Up = ({ by }) => (state, actions) => (
  <button onclick={() => actions.up(by)}>+ {by}</button>
)

export const Down = ({ by }) => (state, actions) => (
  <button onclick={() => actions.down(by)}>- {by}</button>
)

export const Double = () => (state, actions) => (
  <button onclick={() => actions.up(state.count)}>+ {state.count}</button>
)

export const view = (state, actions) => (
  <main>
    <h1>{state.count}</h1>
    <Up by={2} />
    <Down by={1} />
    <Double />
  </main>
)

⚓Reactの「HOC」とは

HOCとは何ぞやという話を小耳に挟んだので。


つっつきボイス:「社内でHOCで悩んでる人がいたので」「自分もよ〜わからん😆」「Qiitaの記事↓だけ見ると、Reactから独立したコンポーネントで云々とありましたが」「コンポーネントのアーキテクチャとかそういう話っぽくはある: Reactをそれなりに使ってる前提の記事っぽい」

参考: React の Higher-order Components の利用方法 - Qiita

コンポーネントを引数にして、カスタマイズしたコンポーネントを返す関数のこと
上記事より

「こういうのはJavaなんかでも昔からあったと思うし、この図↓を見た感じではanonymous inner class的なものに近そう」「日本語だと匿名内部クラスなんですね」「HOCという名前と書式は見慣れないけど、少なくとも突飛な概念ではなさそう🤔


同記事より

参考: 匿名内部クラス - Java 入門

「HOC、どことなく関数型の匂いが😆: Higher-orderとか言ってるぐらいだし」「Rubyでも似たようなことをやろうと思えばできますね(普通やらないけど): 継承を使わないで、そのインスタンスだけを無理やり拡張するみたいな」「Rubyではやらないんですか?」「しないしない😆: 推奨されない書き方」「やっぱりJavaの匿名内部クラスと大して変わらないように見えるけどな〜」

参考: 高階関数 - Wikipedia

「何にしろHOCの位置付けが知りたいな〜: 世の中にいままでなかった新しい概念でHOCという新しい名前がついているのか、はたまた既存の概念を流用しているけど流行らせたいからなんか関数型っぽい名前を付けたのか: 確かにクラス的ではない(Prototypeっぽい)ので、匿名内部クラス(日本語感)とは違う名前になるのまでは分かる」「ですね」「新しい概念ならそのつもりで勉強しないといけないし、実は既存の概念ならそっちに寄せたいし☺」「この年で脳に余分なもの置きたくないし🧠

参考: Object のプロトタイプ - Web 開発を学ぶ | MDN

「おし、ここは社内のJavaScript勢に訊くとするか🤓」「お〜」「じゃ次に行きましょう😆

「HOCをDecoratorだと思えばちょっと腑に落ちるかも🤔」「かもね: Javaのプロトタイプ拡張的な」

参考: [翻訳] Reduxレシピ: Undoヒストリーを実装する - Qiita

「reducerをenhanceするってそれって🤣」「🤣」「そういえば前にRedux記事↓を翻訳したときにはリデューサーとカタカナにするしかありませんでした😅」「要するに高階関数のreduceっぽく、何かを受けたら違うものを返すものをreducerと呼んで、その機能を拡張するものをreducer enhancerと呼んでるってことで🤣」「名前何とかしてくれ🤣

JavaScript: Reduxが必要なとき/不要なとき(翻訳)

「何もしないreducer enhancer(笑)をこうやって拡張する↓: これはちょっとRubyのRackミドルウェアと近いといえば近いかも」「まあRackはちゃんとミドルウェアになってるしクラスが同じメソッドでつながってるけど、こいつは関数ですし😆」「たしかに😆: HOCはもう少し概念が広そう」

// 同記事より
// 何もしない reducerエンハンサはこのような感じです:
function doNothingWith(reducer) {
  return function(state, action) {
    // 渡された reducer を呼ぶだけ
    return reducer(state, action)
  }
}

// 複数の reducer を組み合わせる reducerエンハンサはこのようなものでしょう:

function combineReducers(reducers) {
  return function(state = {}, action) {
    return Object.keys(reducers).reduce((nextState, key) => {
      // 全ての reducer を、それぞれが管理する state の一部とともに呼ぶ
      nextState[key] = reducers[key](state[key], action)
      return nextState
    }, {})
  }
}

参考: Rails と Rack | Rails ガイド

ちょっと安心しました☺

⚓deno: Node.js作者が作ったフレームワーク


deno.landより

以下の記事で知りました。

参考: Node.js における設計ミス By Ryan Dahl - from scratch


つっつきボイス:「Deno、初めて見るけど★とforkはスゴイ」「Nodeの作者だったRyan Dahlさんが、Nodeの反省を込めてスクラッチから作ったそうです」

「出たTOMLのファイル↓😆: TOMLってそこそこ見かけるけど流行ってるんだろうか?」「YAMLじゃなくてTOMLですか」「Tomさんが作ったからTOMLとは」「何という😆

参考: TOML - Wikipedia

# https://github.com/denoland/deno/blob/master/Cargo.tomlより

name = "deno"
version = "0.2.10"
edition = "2018"

[dependencies]
atty = "0.2.11"
dirs = "1.0.4"
flatbuffers = "0.5.0"
futures = "0.1.25"
getopts = "0.2.18"
http = "0.1.15"
hyper = "0.12.23"
hyper-rustls = "0.16.0"
integer-atomics = "1.0.2"
lazy_static = "1.2.0"
libc = "0.2.48"
log = "0.4.6"
rand = "0.6.4"
remove_dir_all = "0.5.1"
ring = "0.14.3"
rustyline = "3.0.0"
serde_json = "1.0.37"
source-map-mappings = "0.5.0"
tempfile = "3.0.5"
tokio = "0.1.15"
tokio-executor = "0.1.6"
tokio-fs = "0.1.5"
tokio-io = "0.1.11"
tokio-process = "0.2.3"
tokio-threadpool = "0.1.11"
url = "1.7.2"

[target.'cfg(windows)'.dependencies]
winapi = "0.3.6"

「そういえばRust言語のCargo↓っていうパッケージ管理システムもTOML使ってた気がする: まあフォーマット選べるんでしょうけど😆」「そういえばGitLabでもどこかでTOMLが使われてた気が」「そもそもTOMLってどこから生えてきたのやら: どことなく昔なつかしのWindows INIファイルっぽくもあるし😆」「それっぽい」「MySQLの設定ファイルも割とこんな感じっすね😆

参考: Cargo.toml vs Cargo.lock - The Cargo Book
参考: config.toml.example · master · GitLab.org / gitlab-runner · GitLab


gitlab.comより

「作者は久々にNode使ってみたら設計の問題がわかってきてえらいすまんかったという感じでDenoを作ったっぽいです」「Deno、聞いたことぐらいはある😆」「New way to JavaScriptと言ってるけど」「TypeScriptを直接実行できるとありますね」「あ、つまり処理系?」「ちなみにDenoはまだまだ未完成だそうです😅

最初に言っておくと、全然まだまだプロトタイプレベルです。 動かないのが普通の状況なので、 lldb でデバッグして直したりしない限り動かないし、ましてやこれでなにか作るのは強くオススメしません。 (※ ちなみに古川はまだ osx で起動どころかビルドに成功していません。)
同記事より

「そういえばNode.jsが登場した頃はサーバーサイドJS何それおいしいの?みたいな感じだったな〜」「Nodeに問題があるとしても今さら大きく変えるわけにもいかないし😆」「別のものを作るしかないでしょうね」

⚓その他JS


つっつきボイス:「この間社内のSlackに貼っていただいてたヤツです」「この記事最高😆」「ポエムとしても優秀🌷」「大好き💕」「オチも素敵すぎる: 結局ESLintルールがっつり書いたんかいって」「オレの過ちを繰り返さないで欲しいという願いがとてもよく伝わってくる」「過去の自分に呼びかけたい感」「結局自分たちはこんなルール知らなくていいんだなって😆: これならESLint使いたくなるし」「このすべてを放出しきった感半端ない」

「『カスンタムインストール』とか勢いのあるタイポもアツい🔥」「これもう直さないって途中から決めたんでしょうね☺: 『ル0ル』とか同じタイポが複数出てきたりしてるし」「『おまえがひっしに書いてきた』とかだんだんひらがなが増えてきたり」「アルジャーノン現象!」

↓古典SFです。翻訳も実に見事。昔「まごころを君に(原題: Charly)」というタイトルで映画になってました。

参考: アルジャーノンに花束を - Wikipedia
参考: まごころを君に - Wikipedia

🌟を進呈いたします。おめでとうございます🎉

⚓CSS/HTML/フロントエンド/テスト

⚓セキュリティ関連ツイート


つっつきボイス:「目についたツイートをいくつか」「そういえば徳丸先生がパスワードの保存方式のアンケートを探すってツイートしてた」

「まあ大昔に作られたようなサービスはだいたいハッシュ化されてませんけど😆」「イントラに守られてるシステムにありがち」「まあイントラでセキュリティ対策やりすぎるのも考えものですが」「にしても限度が😆」「今どきそれはちょっとないな〜😆

「『過去5世代分のパスワードを生で保存しているぞい』って🤣」「生で保存しなくても過去のパスワードを使えないようにできるのに意味わかんない🤣」「パスワードが前と何文字違うかが通知されるシステムとか怪しい🤣」「なぜ似てるとわかる🤣

⚓言語

⚓いきなりその他言語


つっつきボイス:「あれ?『言語』少なっ」「言語のエントリがつっつきタイムに間に合わなくって💦」「選択すると見えるとかじゃないのね☺

「まそれはともかく、コミュニティに参加する意義が圧倒的に大きいのはフリーランスですね」「あ、確かに」「フリーランスのプログラマーがコミュニティに参加してなかったら、マジで相談する相手が誰もいないことになるし」

「あとはエンジニアがほとんどいない会社でプログラム書いてる人とか」「一人情シスとかですね」「自分たちの職場環境はこうやってつっつき会やSlackで聞けば強い人が答えてくれたり情報交換したりできるけど、そうした人たちはコミュニティにも参加しなければ孤立化不可避だし、自分がどのぐらいのレベルかすらわからないし」「うんうん」

参考: 第5回:ひとり情シス覆面座談会--思わず共感、あるある本音トーク - ZDNet Japan

⚓その他

⚓ヤマハのハイエンド遠隔会議スピーカーフォン


つっつきボイス:「この間Slackで、ヤマハのハイエンドスピーカーフォンがすごいという話を見かけたのでどれがそうなのかなと思って」「あああれですか: ヤマハのはすごく高いけどホント頑丈で、今使っているYVC-300↓も社内で相当手荒に扱われてるのにびくともしない」「へ〜」「この種のデバイスは堅牢性が大事ですね🛠」「確かにこういうのをケチったら損しそう」「雑にググると4万円ぐらいか」「高!」「高いけど基本これにしとけと言いたい😎: 会議マイクの質が悪いとビデオ会議がほんとつらいので😭」「わかる〜」

参考: YVC-300 特長

⚓その他のその他


つっつきボイス:「Ruby WeeklyですがMac寄りの記事だったので」「VagrantにmacOSをどんどんインストールできると: macOSはその中でmacOSのVMを動かしていいライセンスになってますので😊」「そうでしたね😀」「しないけど😆



つっつきボイス:「AIがやりたいって色々謎い😆」「学部2年の人から聞かれたら線形代数って答えるしかないかな」「でも、アルゴリズムとかわからなくていいからとにかくAIを触ってみたい!って人なら、Jupyter Notebookで何か動かす方法とかチュートリアルとか教えてあげれば満足してもらえるんじゃ?」「別にすごい人になりたいとかじゃないでしょうし😆」「その方が双方幸せになれそうですね😋」「と自分ならアドバイスするかな☺

参考: データ分析で欠かせない!Jupyter Notebookの使い方【初心者向け】 | TechAcademyマガジン



つっつきボイス:「とりあえず名前空間を汚染すれば何とかなるみたいな😆」「証明書を検索できるcrt.shを使うと、新商品とか新企画とかが世に出る前にいち早く検出できてしまういたいな話を以前教わって記事に書いたのを思い出しました↓」

Let’s EncryptがVerisignと棲み分けできる理由: SSL証明書の「DV、OV、EV」とは何か

⚓番外

⚓フロベニウスの硬貨交換問題


つっつきボイス:「これはいい問題😍」「問題はいいんですけど、問題文の「5と8の和」が小学生には難しいんじゃないかと思って😆」「え、これって上限あるんだ😅」「『5円玉と8円玉では払えない金額の最大値は?』っていう問題ならわかりやすいのに、とFacebookで教わったんですが、実はもともとコインを使う問題だったそうです」「上みたいにリストアップすると理解できるから、確かに小学4年生ぐらいによさそう」「私はExcelでリスト作って色塗って解きました😊」「ひらめきを試すのにいい感じ👍

⚓遠隔でレーザーで耳に音を届ける


つっつきボイス:「耳元でいきなり声が聞こえたら、地縛霊でもいるのかとビックリして飛び退いちゃいそうです: 心臓に悪い😇」「超指向性のスピーカーみたいな?」「それをレーザーでやってる」「出力を上げたら物を動かしたりもできたりして」「既にアメリカの軍が駆逐艦か何かで採用してましたね: 空母一隻より高いとかで採算がまるで合わなくてディスコンになったそうですが😆」「ずこー😆」「おあとがよろしいようで」

参考: 遠くからレーザーで「耳元にささやく」技術が開発。2.5m離れた場所で60dBの音声を発生 - Engadget 日本版


今回は以上です。

おたより発掘

週刊Railsウォッチ(20190204)あってうれしい40のgem、Ruby 2.6.1セキュリティリリース、Hanami v2.0.0.alpha1リリースほか

今回も良かった。deno は rebuld でも話題にでて気になっている。

2019/02/06 22:20

バックナンバー(2019年度第1四半期)

週刊Railsウォッチ(20190128)Rails 6のオートローダーがZeitwerkに置き換わる?Rails 6はRuby 2.5が必須、最近のSQLiteほか

今週の主なニュースソース

ソースの表記されていない項目は独自ルート(TwitterやRSSなど)です。

Ruby 公式ニュース

Rails公式ニュース

Ruby Weekly

RubyFlow

160928_1638_XvIP4h

Hacklines

Hacklines

Publickey

publickey_banner_captured

Postgres Weekly

postgres_weekly_banner

DB Weekly

db_weekly_banner

Rails: memory_profilerでRuby文字列の重複を削減する(翻訳)

$
0
0

概要

原著者の許諾を得て翻訳・公開いたします。

日本語タイトルは内容に即したものにしました。

Rails: memory_profilerでRuby文字列の重複を削減する(翻訳)

皆さんのRailsアプリで文字列ががっつり重複している可能性があります。本記事ではそうした重複を取り除く(以下dedup)方法について説明します。


RubyやRailsで非常によくある問題のひとつが、メモリ使用量です。アプリをサイトにホスティングすると、たいていの場合パフォーマンスよりもメモリの方がボトルネックになります。Discourseではアプリのチューニングにかなりの時間を費やし、メモリが1GBもあればDiscourseを十分セルフホスティングできるようにしました。

私は、メモリ使用量のデバッグに役立つmemory_profiler gemを作成しました。これを使えばアプリのメモリ使用量を簡単に取れるようになります。皆さんのRailsアプリでもmemory_profiler gemを試してみることを強くおすすめします。こんなに多くの成果が、すぐ手の届くところにぶらさがっていることに驚くことでしょう。1日もかければ、最適化されていないアプリのメモリ使用量を20〜30%節約できることも珍しくありません。

memory_profilerは、メモリ使用量を以下の2つのセクションに分けてレポートを生成します。

  • Allocated memory(割り当てられたメモリ): 測定中のブロックで割り当てられたメモリ量
  • Retained memory(保持メモリ): ブロックの測定完了直後の使用中のメモリ量

以下のコード例で説明します。

def get_obj
   allocated_object1 = "hello "
   allocated_object2 = "world"
   allocated_object1 + allocated_object2
end

retained_object = nil

MemoryProfiler.report do
   retained_object = get_obj
end.pretty_print

上を実行すると以下のレポートが出力されます。

[a lot more text]
Allocated String Report
-----------------------------------
         1  "hello "
         1  blog.rb:3

         1  "hello world"
         1  blog.rb:5

         1  "world"
         1  blog.rb:4

Retained String Report
-----------------------------------
         1  "hello world"
         1  blog.rb:5

私たちは一般的に、プロセスのメモリ使用量を削減する場合はretained memoryの削減に注力し、ホットコードパス(実行頻度の高いコード)を最適化する場合はallocated memoryの削減に注力することにしています。

本記事では、retained memory、特にStringの最適化に注力することを目標とします。

Railsアプリのメモリプロファイラレポートを出力する方法

Railsをプロファイリングするために、起動時に以下のスクリプトを用います。

if ENV['RAILS_ENV'] != "production"
  exec "RAILS_ENV=production ruby #{__FILE__}"
end

require 'memory_profiler'

MemoryProfiler.report do
  # ファイルは/scriptsディレクトリに出力する前提(適宜調整)
  require File.expand_path("../../config/environments", __FILE__)

  # Railsルーターのウォームアップが必要
  Rails.application.routes.recognize_path('abc') rescue nil

  # ローカライズ関連で用いるyamlをmasterプロセスに読み込む
  I18n.t(:posts)

  # 全モデルを読み込んでActiveRecordの内部キャッシュをウォームアップする
  (ActiveRecord::Base.connection.tables - %w[schema_migrations versions]).each do |table|
    table.classify.constantize.first rescue nil
  end
end.pretty_print

これで以下のようなレポートを取れます(Gist)。

# memory_profile.txt
Total allocated: 200134661 bytes (2120673 objects)
Total retained:  33789989 bytes (291785 objects)

allocated memory by gem
-----------------------------------
  72565994  activesupport-5.1.4
  17893868  actionpack-5.1.4
  17293551  psych
  12181501  activerecord-5.1.4
  11900863  activemodel-5.1.4

なお上の結果は切り詰めてあります(オリジナル

メモリ使用量最適化作業を始めた早々に、保持メモリの中でStringが極めて高い割合を占めていることがわかりました。memory_profilerには、Stringの使用量削減に便利なように専用のStringセクションを設けてあります。

上のレポートの場合、以下のように出力されます。

Retained String Report
-----------------------------------
       942  "format"
       940  /home/sam/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/actionpack-5.1.4/lib/action_dispatch/journey/nodes/node.rb:83
         1  /home/sam/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/actionpack-5.1.4/lib/action_controller/log_subscriber.rb:3
         1  /home/sam/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/activemodel-5.1.4/lib/active_model/validations/validates.rb:115

       941  ":format"
       940  /home/sam/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/actionpack-5.1.4/lib/action_dispatch/journey/scanner.rb:49
         1  /home/sam/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/activesupport-5.1.4/lib/active_support/dependencies.rb:292
... 省略 ...

"format"という文字列がRubyヒープに940箇所もばらまかれていることがわかりました。これらの文字列はいずれも「root化」されているので、ヒープからGCされずに居座っています。Railsがこれを940個も必要としていることから考えて、コントローラのどのparamsがこれを取得しているのかはすぐに特定できます。

RubyのRVALUE(Rubyヒープ上のスロットを指す一意のobject_id)は、x86では40バイトを消費します。「format」という文字列はかなり短いので、余分なポインタやmallocがなくても1つのRVALUEに収まります。それなのに「format」というたったひとつの文字列を保存するのに37,600バイトも使われています。これは明らかに無駄なので、私たちからRailsにプルリク(#32016)を送らないとですね(訳注: 既にmergeされています)。

他にもいくつか無駄が見つかりました。

  1. フルGCが実行されるたびにRubyヒープ上のあらゆるオブジェクトがスキャンされる。このスキャンはプロセスが最終的に死ぬまでずっと繰り返される。
  2. メモリの小さなチャンクがプロセスのアドレス空間に収まりきらないため、時間とともにメモリ断片化が生じ、RVALUEヒープ同士のギャップによってRVALUEの40バイトの実際のインパクトが増大する可能性がある。

  3. Rubyヒープが増大するに連れて、ただちに増大が加速する(対応方法については#12967を参照)

  4. あるRubyヒープ内で1つのRVALUERVALUEを500個ほど含んでいると、再利用されないことがある。

  5. オブジェクト数が増加すると、CPUキャッシュの効率が低下してスワップ発生率の増加などにつながる。

文字列をdedupする方法

ご興味のある方向けに、Ruby 2.5以降で文字列のdedupに使える手法のおおよそのニュアンスをお伝えするgistを作成しました。少々時間をかけてじっくり読むことをおすすめします。

require "active_support"
require "active_support/core_ext/string/output_safety"
require "objspace"

def assert_same_object(x, y)
  raise unless x.object_id == y.object_id
end

def assert_not_same_object(x, y)
  raise unless x.object_id != y.object_id

説明の欲しい方向けに、ここで使える方法をいくつかご紹介します。

1. 定数を使う

# 変更前
def give_me_something
   "something"
end

# 変更後
SOMETHING = "something".freeze

def give_me_something
   SOMETHING
end
  • メリット:
    • すべてのバージョンのRubyで使える
  • デメリット:
    • 冗長かつ美しくない
    • freezeのマジックを付け忘れると、Ruby 2.3以降では文字列を正しく使い回せない可能性がある

2. # frozen_string_literal: trueマジックコメントを付ける

# 修正前
def give_me_something
   "something"
end

# 修正後

# frozen_string_literal: true
def give_me_something
   "something"
end

Ruby 2.3ではfrozen_string_literal: trueプラグマが導入されました。ファイルの冒頭行に# frozen_string_literal: trueと記述すると、Rubyのファイルの扱い方が変わります

単純な文字列リテラルはすべてfreezeおよびdedupされます。

式展開文字列もすべてfreezeされますが、dedupはされません。たとえば、x = "#{y}"はfrozenかつdedupされない文字列となります。

私はこれがRubyのデフォルトとなるべきだと思いますし、Railsを含む多くのプロジェクトでも受け入れられるべきだとも思います。Ruby 3.0ではこれがデフォルトになればと願っています。

  • メリット:
    • 非常に使いやすい
    • 見苦しくない
    • 長期的な最適化の自由度が落ちない
  • デメリット:
    • 既存のファイルへの適用が面倒になる可能性(テストスイートをしっかり書いておくことを強く推奨)

落とし穴

気をつけたい落とし穴がいくつかあります。最大の落とし穴はString.newのデフォルトエンコーディングです。

buffer = String.new
buffer.encoding => Encoding::ASCII-8BIT

# vs

# @+によるStringのunfreezeはRuby 2.3以降で導入された
buffer = +""
buffer.encoding => Encoding::UTF-8

文字列に何かを追加するとエンコーディングがその場で変わるので、通常はまったく問題になりません。しかし、空文字列への参照をサードパーティのライブラリに渡すと大惨事になりますので、"".dup+""を使う良い習慣をつけましょう。

3. 動的に文字列をdedupする

Ruby 2.5から、文字列をdedupする新しい手法が導入されました。これはEric Wongによって#13007で導入されました。

Matzの発言を引用します。

さしあたって、rb_fstringの呼び出しに#-を使うことにしよう。
ユーザーがもっとわかりやすいメソッド名を望んでいるようなら、後で議論しよう。
個人的にはfstringとしたくない。

つまり、String#-メソッドを使うと文字列を動的にdedupできるのです。

a = "hello"
b = "hello"
puts ((-a).object_id == (-b).object_id) # Ruby 2.5ではtrue(通常は)

この構文はRuby 2.3以降で利用できますが、この最適化を利用できるのはRuby 2.5以降のみです。

この手法は、dedupされる文字列は引き続きGCの対象になるという意味で安全です。

この機能は、dedupされた文字列を保持する以下のハッシュテーブルに依存します。これはある時期からRubyに存在しています。

static VALUE
 register_fstring(VALUE str)
 {
VALUE ret;
st_table *frozen_strings = rb_vm_fstring_table();

do {
    ret = str;
    st_update(frozen_strings, (st_data_t)str,
          fstr_update_callback, (st_data_t)&ret);
} while (ret == Qundef);

assert(OBJ_FROZEN(ret));
assert(!FL_TEST_RAW(ret, STR_FAKESTR));
assert(!FL_TEST_RAW(ret, FL_EXIVAR));
assert(!FL_TEST_RAW(ret, FL_TAINT));
assert(RBASIC_CLASS(ret) == rb_cString);
return ret;
}

以前、このテーブルは"string".freezeの最適化とハッシュキーの自動dedupに用いられていました。この機能はRuby 2.5で初めて一般に知られました。

これは、重複のあるコンテンツ(Railsのルーティングなど)を持つ入力のパースや、動的なルックアップテーブルの生成で実に有用です。

しかし、いいことばかりではありません🌹(訳注: not all rosesにかけたシャレ)。

まず、(訳注: #-という書式が)あまりに見苦しいという美的観点からこの構文への批判が一部で巻き起こり、利用を拒否する人もいます。

さらに、この手法には落とし穴がどっさりあります。これについてはgistにみっちりと記載しています。

#14478が修正されるまでは文字列をunfreezeする前にdedupする必要がある

訳注: 現在は反映されています

yuck = "yuck"
yuck.freeze
yuck_deduped = -+yuck

文字列がtaintされている場合は「一部」しかdedupできない

これは、文字列が長い場合にVMによって共有文字列が作成されるにもかかわらず、RVALUEを引き続き保持するということです。

love = "love"
love.taint
(-love).object_id == love.object_id

# dedupするためにはコピーが必要
deduped = -love.dup.untaint

困るのは、この修正を適用したい場所の多くで、taintされた文字列を残すかどうかという選択を迫られることです。古典的な例としては、RailsのPostgreSQLアダプタが"character varying"制限付き可変長文字列)のコピーを142個持つことがDiscourseの上述のレポートで判明しています。場合によってはこの制約のために、dedupしたい文字列で無意味なコピーを余分に持つ羽目になることがあります(この機能を使うためにuntaintするのは絶対イヤだという人がこの宇宙に3人はいるので)。

個人的には、この嫌らしいtaintがらみのコードをRubyのコードベースから完全に消し去れればいいのにと思っています🔥。そうできれば、どちらもずっとシンプルかつ安全かつ高速になることでしょう。

文字列にインスタンス変数が定義されていると一部しかdedupできない

# html_safeは文字列にインスタンス変数を設定するのでdedupできない
str = -"<html>test</html>".html_safe

この特殊な制限は回避できないので、Rubyの力でどうにかできるのかどうか私には何とも言えません。htmlの断片をdedupしようとすると、文字列は共有できても、完全なdedupはできないというつらみが生じます。

参考

皆さんが本記事を用いてアプリのメモリ使用量をうまく削減できることをお祈り申し上げます。

関連記事

Ruby: メソッドを最速でプロファイリングする方法(翻訳)

Ruby: mallocでマルチスレッドプログラムのメモリが倍増する理由(翻訳)

Viewing all 1381 articles
Browse latest View live