Quantcast
Channel: JavaScriptの記事一覧|TechRacho by BPS株式会社
Viewing all 164 articles
Browse latest View live

Vue.jsサンプルコード(36)Deleteキーで前のフィールドに戻って消す

$
0
0

36. Deleteキーで前のフィールドに戻って消す

  • Vue.jsバージョン: 2.5.2
  • 日本の携帯電話番号の入力を想定しています(3桁、4桁、4桁)。
  • フィールドに入力すると、桁数いっぱいになったときに次のフィールドにフォーカスが自動で移動します。
    • 最後のフィールドは桁数いっぱいに入力してもフォーカスは移動しません。
  • Deleteキーでフィールドの数字を消すと、前のフィールドにフォーカスが戻り、さらに1文字消します。
    • 最初のフィールドはDeleteキーですべて消してもフォーカスは移動しません。
  • 画面をリロードすると最初に戻ります。

サンプルコード

ポイント

前のフィールドに戻ったときに1文字消したくない場合は、以下の2か所の@keydown@keyupに変更します。

<input class="form-control" size="3" v-model="a" ref="r1" />
 -
<input class="form-control" size="4" v-model="b" ref="r2" @keydown.delete="b2" />
 -
<input class="form-control" size="4" v-model="c" ref="r3" @keydown.delete="b3" />

バックナンバー(Vue.jsサンプルコード)

Vue.jsサンプルコード(01〜03)Hello World・簡単な導入方法・デバッグ・結果の表示とメモ化


Vue.jsサンプルコード(37)一定時間経過後に自動でリダイレクトする

$
0
0

37. 一定時間経過後に自動でリダイレクトする

  • Vue.jsバージョン: 2.5.2
  • 30カウントすると直前のページに自動でリダイレクトします。
    • 以下のJSFiddle埋め込みはフレーム内のみリダイレクトします。
    • ブラウザによってはこのページの直前のページにリダイレクトする可能性もあります。
  • 画面をリロードすると最初に戻ります。

サンプルコード

ポイント

createdは、new Vue のあとで1回呼ばれます。

const vm = new Vue({
  el: "#app",
  data: {a: 30},
  created: function() {
    setInterval(() => { this.a-- }, 1000)
  },
  watch: {
    a: function(v) {
    if (v <= 0) {
        document.location = "/"
      }
    }
  }
})

バックナンバー(Vue.jsサンプルコード)

Vue.jsサンプルコード(01〜03)Hello World・簡単な導入方法・デバッグ・結果の表示とメモ化

週刊Railsウォッチ(20180420)NGINX Unit正式リリース、Rubyをメモリリークさせてみる、美しいコード宇宙ほか

$
0
0

こんにちは、hachi8833です。記事を絞ろうと気張るのを諦めて単に絞ることにしました。

昨日のつっつきは、BPSの福岡拠点であるウイングドアの皆さまとZoomで画面共有しながら行いました。おかげさまでさらに盛り上がりました。ありがとうございます。

ところが何ということでしょう、録音を忘れていた私でした…😢今週のつっつき成分は少なめです。

というわけで、季節の変わり目のウォッチ、いってみましょう。

各記事冒頭には⚓でパーマリンクを置いてあります: 社内やTwitterでの議論などにどうぞ

⚓Rails: 今週の改修

Rails 6になるmasterブランチから見繕いました。

先週取り上げたようなsplat外し的修正が目立ちます。86cafe7なんかもそうですね。

⚓RedisCacheStore#delete_matchedでブロックが発生してタイムアウトしていたのを修正

# commitより
 def delete_matched(matcher, options = nil) 
   instrument :delete_matched, matcher do 
     case matcher 
     when String 
       redis.with { |c| c.eval DELETE_GLOB_LUA, [], [namespace_key(matcher, options)] } 
     else 
       raise ArgumentError, "Only Redis glob strings are supported: #{matcher.inspect}" 
     end 
   end 
 end 

つっつきボイス: 「Lua言語が使われてる部分があったんだ…」

参考: Lua - Wikipedia

⚓javascript_include_tagヘルパーにnonce: trueオプションを追加

# actionview/lib/action_view/helpers/asset_tag_helper.rb#L
       def javascript_include_tag(*sources)
         options = sources.extract_options!.stringify_keys
         path_options = options.extract!("protocol", "extname", "host", "skip_pipeline").symbolize_keys
@@ -90,6 +95,9 @@ def javascript_include_tag(*sources)
           tag_options = {
             "src" => href
           }.merge!(options)
+          if tag_options["nonce"] == true
+            tag_options["nonce"] = content_security_policy_nonce
+          end
           content_tag("script".freeze, "", tag_options)
         }.join("\n").html_safe

つっつきボイス:nonceって前にもつっつきで扱った気が」「number used onceのことだったんかー↓」「同じようなCSP属性があったけど何だったかな」「ところで今ぐぐって見つけていただいたreference.hyper-text.orgってちょっといい感じのリファレンスサイトですね」

‘nonce-
nonce 属性は、CSP(Content Security Policy)によって、文書内に読み込まれた script 要素や、style 要素の内容を実行するかを決定するために利用される nonce(number used once / ワンタイムトークン) を指定します。
reference.hyper-text.orgより

参考: nonce 属性 | HTML5 タグリファレンス | W3 Watch Reference

参考: CSP: script-src - HTTP | MDN

⚓Content Security Policyでリクエストごとのnonceを1つだけにする

これもnonceですね。

# actionpack/lib/action_dispatch/http/content_security_policy.rb#L232
-      def build_directives(context)
+      def build_directives(context, nonce)
         @directives.map do |directive, sources|
           if sources.is_a?(Array)
             "#{directive} #{build_directive(sources, context).join(' ')}"
+            if nonce && nonce_directive?(directive)
+              "#{directive} #{build_directive(sources, context).join(' ')} 'nonce-#{nonce}'"
+            else
+              "#{directive} #{build_directive(sources, context).join(' ')}"
+            end
           elsif sources
             directive
           else
...

⚓APIドキュメントの修正3種

# activemodel/lib/active_model/validations/inclusion.rb#L21
       #   class Person < ActiveRecord::Base
-      #     validates_inclusion_of :gender, in: %w( m f )
+      #     validates_inclusion_of :role, in: %w( admin contributor )
       #     validates_inclusion_of :age, in: 0..99
       #     validates_inclusion_of :format, in: %w( jpg gif png ), message: "extension %{value} is not included in the list"
       #     validates_inclusion_of :states, in: ->(person) { STATES[person.country] }

今どきgenderが男と女だけというのは古臭い感じかもですね。
そういえばだいぶ昔は「クライアント/サーバー」と言わずに「マスター/スレーブ」って言ってたんですよね。「主人と奴隷」はさすがに200年遡る感じです。


つっつきボイス: 「へー、LGBTコンシャスというか」「そういえばこの間はてブであがってたISO 5218が、まさにこうした用途のための国際規格ですよ」「知らなかったー!」

  • 0 = not known(不明)
  • 1 = male(男性)
  • 2 = female(女性)
  • 9 = not applicable(適用不能)

ブクマにyuguiさんのコメントを見つけました:

システムで「性別」の情報を扱う前に知っておくべきこと – Qiita

「入力して何の役に立つの?」という最重要ポイントを読み落とさないでほしい。医療かマーケか戸籍処理か本人確認か、用途に応じて入力させるべきかも提示選択肢も入力欄ラベルも自ずと定まり、難しいことはない

2018/04/13 06:55

参考: システムで「性別」の情報を扱う前に知っておくべきこと - Qiita — これ必読ですね

遠い昔、子どもの頃の遠足で訪れたお寺で他の子が「観音様って男?女?」と質問すると、お寺の管理人さんが「観音は 男にあらず 女にあらず」と迷いなく答えたのをふと思い出しました。典拠を探そうと思ってググると新興宗教系サイトがわらわら出てきた…


# actionview/lib/action_view/context.rb#L10
   # Action View contexts are supplied to Action Controller to render a template.
   # The default Action View context is ActionView::Base.
   #
-  # In order to work with ActionController, a Context must just include this module.
-  # The initialization of the variables used by the context (@output_buffer, @view_flow,
-  # and @virtual_path) is responsibility of the object that includes this module
-  # (although you can call _prepare_context defined below).
+  # In order to work with Action Controller, a Context must just include this
+  # module. The initialization of the variables used by the context
+  # (@output_buffer, @view_flow, and @virtual_path) is responsibility of the
+  # object that includes this module (although you can call _prepare_context
+  # defined below).

つっつきボイス: 「クラス名としてはActionControllerだけど名称としてはAction Controllerが正式ってことなんでしょうか?」「んー、というより冒頭にAction Viewって書いてあるから単にそれと書式を合わせたってことなんじゃ?」「あなるほど、ドキュメントのローカルレベルでの統一というか」

くっついてる方が検索しやすいかなとちょっぴり思ったりしました。


# guides/source/_welcome.html.erb#L18
 <% end %>
 <p>
 The guides for earlier releases:
+<a href="http://guides.rubyonrails.org/v5.2/">Rails 5.2</a>,
 <a href="http://guides.rubyonrails.org/v5.1/">Rails 5.1</a>,
 <a href="http://guides.rubyonrails.org/v5.0/">Rails 5.0</a>,
 <a href="http://guides.rubyonrails.org/v4.2/">Rails 4.2</a>,

つまりRuby on Rails 5.2 Release Notesが正式になったということですね。Railsガイドの翻訳の優先順位上げます。ご期待ください。


railsguides.jpより

⚓サーバーIP指定時のHOST環境変数のサポート廃止

# 
-            ENV.fetch("HOST", default_host)
+
+            if ENV["HOST"] && !ENV["BINDING"]
+              ActiveSupport::Deprecation.warn(<<-MSG.squish)
+                Using the `HOST` environment to specify the IP is deprecated and will be removed in Rails 6.1.
+                Please use `BINDING` environment instead.
+              MSG
+
+              return ENV["HOST"]
+            end
+
+            ENV.fetch("BINDING", default_host)

SuSeSUSEでは$HOSTがデフォルトで設定され、$HOSTNAMEと等しくなっている。
https://www.suse.com/documentation/sled11/book_sle_admin/data/sec_adm_variables.html
従って、デフォルトではlocalhostではなくhostnameにバインドされる。これはデフォルトの振る舞いとしてはふさわしくないように思える。
この環境変数名の利用を避けるために、環境変数をHOSTからBINDINGに変更する。
同コミットより


つっつきボイス: 「お、これ大事な修正ですね: Rails 6.1からはHOSTS環境変数のサポートが削除されるのでBINDING環境変数を使うように、と」

「話逸れますが(逸れてばっかり)、昔いた会社のヨーロッパ系社員がSuSeSUSEを「ズーゼ」ってドイツ語っぽく発音してて、最初何のこと?って思っちゃいました」「へー、SuSeSUSE Linuxって日本ではなぜかそれほど知名度ないけど、ヨーロッパでは長い間ずっと主流ですね」「シンボルカラーが緑色だったのだけ覚えてます: 今もそうなのかな?」


suse.comより

⚓storageフォルダに.keepファイルを追加する条件を修正

# railties/lib/rails/generators/rails/app/templates/gitignore.tt
 <% unless skip_active_storage? -%>
 # Ignore uploaded files in development
 /storage/*
-
+<% if keeps? -%>
+!/storage/.keep
 <% end -%>
+<% end -%>
+
 <% unless options.skip_yarn? -%>
 /node_modules
 /yarn-error.log

最初にRailsを触ったときに、空のフォルダに入っている.keepって何だろう?と思ったのを思い出しました。


つっつきボイス: 「ははぁ、.gitignore/storage/.keep登録しちゃってたのか: そりゃ直さなきゃ!」「空ディレクトリにファイルが何もないとディレクトリがGitに登録されないんで、.keepが置かれてるんですね」「そうそう、前に教えてもらって知りました」

⚓Rails

⚓Action Policy: Ruby/Rails向けauthorizationフレームワーク


元記事より

authorizationの訳語でいつも悩んでます。「認証(authentication)」と対にして「認可」「承認」とされることが多いのですが、認可でも何かこうしっくりこない…リソース管理とかリソースのアクセス権の扱いなんかがauthorizationなんですよね。


つっつきボイス: 「これもEvil Martiansさんがスポンサーになってる: Evil MartiansさんのブログはTechRachoの翻訳記事にときどき使わせてもらってるんですが、こうやってちょっと先を行くようなgemのスポンサーになっているのをちょくちょく見かけるので」「action_policy gemはEvil Martiansのリポジトリとは別の場所なのか: それにしてもevil-seedとかevil-clientとか、RailsのGemfileとかで見かけたらドキーッっとしそうw」「徹底しているというか、旗色は鮮明ですね」

# 同リポジトリより
class PostPolicy < ApplicationPolicy
  # everyone can see any post
  def show?
    true
  end

  def update?
    # `user` is a performing subject,
    # `record` is a target object (post we want to update)
    user.admin? || (user.id == record.user_id)
  end
end

「で、このaction_policyはコントローラ側に寄せた設計のようだ」「以前から悩んでるところなんだけど、authorizationってPundit式にモデルに寄せるべきか、cancancan式にコントローラというかURL側に寄せるべきか、どっちだと思う?」「そうなんすよねぇ…モデル側の方がライブラリとしておさまりはいいのかなと思ったりもしますが、複雑になってくるとつらくなってくるし、果たしてモデルの責務なんだろうかという疑問も残るし」「自分は何となくだけど、コントローラ側の方が自然な気がするんだけどなー」「cancancanは機能が多すぎて逆にツライ: コントローラでauthorize!ってできればもうそれでいいのっ!w」「住処が定まらない感じですね」

新しいRailsフロントエンド開発(1)Asset PipelineからWebpackへ(翻訳)

⚓NGINX Unit正式リリース

新しいRubyアプリサーバー「NGINX Unit」を調べてみた(翻訳)


つっつきボイス: 「どうも思い違いされていることが多いみたいなんですが、NGINX UnitはNGINXとは別物なんですよねー」「どちらもNGINX社の製品なのにそうなんですね: 紛らわしい…」



nginx.comより

⚓社内Slackより: 悪例サンプルコードづくりは難しい

SOLID Principles #4 - Interface Segregation Principle  | Netguru Blog on Ruby/Ruby on Railsを翻訳したときに、サンプルコードについて社内Slackでかなり議論になりました。
私も訳していて、原著者が非常に苦しみながら書いていることを痛感しました。

# 同記事の悪例コード
class Computer 
  def turn_on; end
  def type; end
  def replace_cpu; end
  def change_user_password; end
end

class ComputerUser
  def initialize
    @computer = Computer.new
  end
end

class Programmer < ComputerUser
  def write_code
    @computer.turn_on
    @computer.type
  end
end

class Administrator < ComputerUser
  def update_user
    @computer.turn_on
    @computer.change_user_password
  end
end

class Technician < ComputerUser
  def fix_computer
    @computer.replace_cpu
    @computer.turn_on
  end
end

記事として練り直しを考えているところです。Slackでの指摘をまとめると以下のような感じでした。

  • サンプルコードの修正前と修正後の差が大きすぎて、ISPにフォーカスしにくい
  • そもそも修正前のコードがいろいろひどすぎて、そっちに気を取られてしまう
  • 可能なら修正したいところだけど、 ユースケースが見えない

推測ですが、教科書にありがちな哺乳類だの何だのという浮世離れしたクラスではなく、何とかして現実的なクラスにしようとしたように思えます。

ISPを説明するのに向いたサンプルコードがあれば著者もぐっと楽になりそうです。手頃な素材がどこかにないものでしょうか。

⚓RailsでのExplicit Contract

# 同記事より
class StudentsController < ApplicationController

  # ...

  def pull
    pulled_student_list = third_party_contract.fetch_students['students']

    pulled_student_list.each do |pulled_student|
      student = Student.new
      student.name = pulled_student['name']
      student.age = pulled_student['age']
      student.save!
    end
  end

  private

  def third_party_contract
    Rails.configuration.x.third_party_contract
  end
end

つっつきボイス: 「third party contract? どうやら法律用語から来てるっぽい?わからんけど」「httpartyっていうHTTPクライアントgem使ってますね: ★5,000近い」

# 同リポジトリより
# Use the class methods to get down to business quickly
response = HTTParty.get('http://api.stackexchange.com/2.2/questions?site=stackoverflow')

puts response.body, response.code, response.message, response.headers.inspect

# Or wrap things up in your own class
class StackExchange
  include HTTParty
  base_uri 'api.stackexchange.com'

  def initialize(service, page)
    @options = { query: { site: service, page: page } }
  end

  def questions
    self.class.get("/2.2/questions", @options)
  end

  def users
    self.class.get("/2.2/users", @options)
  end
end

stack_exchange = StackExchange.new("stackoverflow", 1)
puts stack_exchange.questions
puts stack_exchange.users

「httpartyで思い出したけど、herっていうgemもなかなかよくできてますヨ(前にも話したけど)」

# 同リポジトリより
User.all
# GET "https://api.example.com/users" and return an array of User objects

User.find(1)
# GET "https://api.example.com/users/1" and return a User object

@user = User.create(fullname: "Tobias Fünke")
# POST "https://api.example.com/users" with `fullname=Tobias+Fünke` and return the saved User object

@user = User.new(fullname: "Tobias Fünke")
@user.occupation = "actor"
@user.save
# POST "https://api.example.com/users" with `fullname=Tobias+Fünke&occupation=actor` and return the saved User object

@user = User.find(1)
@user.fullname = "Lindsay Fünke"
@user.save
# PUT "https://api.example.com/users/1" with `fullname=Lindsay+Fünke` and return the updated User object

⚓ChromebookでRailsが動く?

参考: ChromebookのFile Systemへの書込み制限を解除する方法 ― Write-Protect-Screwの外し方 C300MA編 | Chrome速報

Chromebrew(crewコマンド)というのを入れると様々なシステムを簡単にインストールできる。EmacsもNodeも簡単にインストールできた。Sinatraも使えたのでWebアプリ開発もできてしまう。頑張ればRailsも動きそうである。(crewだとコケたが個別インストールすれば大丈夫そう)


つっつきボイス: 「ポイントは、Chromebookのフタを外してプロテクトスイッチを解除することみたいです」「DockerがChromebookで動けばいいんじゃね?そしたらもう何でも動くよ♥

⚓その他記事など


つっつきボイス:AssetSyncはAWSのS3と同期するgemだけど、Azureにも対応したのか」

参考: Microsoft Azure Cloud Computing Platform & Services

⚓Ruby trunkより

⚓IRBのパーサーをRipperにした


同パッチより

⚓===match?より遅い

# 同issueより
# ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-darwin17]

require 'benchmark/ips'

def run_benchmark(title, **benchmarks)
  puts '', title, '=' * title.size, ''

  # Validation
  benchmarks.each do |benchmark_name, benchmark_fn|
    puts "#{benchmark_name} result: #{benchmark_fn.call()}"
  end

  puts

  Benchmark.ips do |bm|
    benchmarks.each do |benchmark_name, benchmark_fn|
      bm.report(benchmark_name, &benchmark_fn)
    end

    bm.compare!
  end
end

regex = /foo/
# => /foo/
string = 'foobarbaz'
# => "foobarbaz"

run_benchmark('=== vs match? - 2.5.1',
  '===':    -> { regex === string },
  'match?': -> { regex.match? string }
)

=== vs match? - 2.5.1
=====================

=== result: true
match? result: true

Warming up --------------------------------------
                 ===   173.435k i/100ms
              match?   233.124k i/100ms
Calculating -------------------------------------
                 ===      3.174M (± 1.6%) i/s -     15.956M in   5.029027s
              match?      5.626M (± 2.5%) i/s -     28.208M in   5.016991s

Comparison:
              match?:  5626170.1 i/s
                 ===:  3173659.6 i/s - 1.77x  slower

つっつきボイス: 「まず===match?って同一なんだっけ違うんだっけ?」「ほむほむ、match?は位置を指定できるのと、例の特殊変数を更新しないところが違う、と↓」「特殊変数を更新してたらその分遅くなりそうですね」「$~とか使うとRubyがPerlチックになるんで好きじゃないなー」「私も」

  • Regexp#===: 文字列 string との正規表現マッチを行います。 マッチした場合は真を返します。string が文字列でもシンボルでもない場合には false を返します。このメソッドは主にcase文での比較に用いられます。
  • Regexp#match?: 指定された文字列 str に対して 位置 pos から自身が表す正規表現によるマッチングを行います。 マッチした場合 true を返し、マッチしない場合には false を返します。また、$~ などパターンマッチに関する組み込み変数の値は変更されません
    Rubyリファレンスマニュアルより

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

⚓Windowsで削除の制御文字が期待どおりに動かない

ruby -e 'puts "Hello world!\e[D\e[D\e[K\nWhat is up?"'
ruby -e 'puts "Hello world!\e[D\e[D\e[0K\nWhat is up?"'
ruby -e 'puts "Hello world!\e[D\e[D\e[1K\nWhat is up?"'

# 期待
Hello worl
Whats up?
Hello worl
Whats up?
           !
Whats up?

# 実際
          d!
What is up?
          d!
What is up?
          d!
What is up?

つっつきボイス: 「普段Web開発しているとほとんどやらないけど、Windowsの生の環境でRubyを使ってる人って、実は結構いるんじゃないかしら: 社内業務改善の手作りツールとかで」「それ私マジでやってました: Excelがバージョンアップするたびに手作りVBAが動かなくなるのが嫌になって、ExcelをRubyで制御する拡張をインストールして動かしたのがRubyとの最初の出会い」「WindowsユーザーのサポートはRubyのありがたい点」

どうやったかもう思い出せませんが、require 'win32ole'的にやったのかもしれません。確かRubyのバージョンは1.9だったはず。

参考: Rubyist Magazine - VBA より便利で手軽 Excel 操作スクリプト言語「Ruby」へのお誘い (前編)

⚓rangeの終わりを「無指定」にできるようになる

今日見つけたツイートです。

ary[1..]   # [1..-1] と同等

その後#14700で早速バグ上がってました。

⚓Ruby

⚓Rubyでメモリーリークを引き起こす方法(Ruby Weeklyより)

# 同記事より
a = []
b = {}

loop {
  sleep(1)

  10_000.times { a << "abc" }

  puts GC.stat(b)[:heap_live_slots]
}

つっつきボイス: 「おー、漏れてる漏れてる」「sleep(1)って必要なんだろうか?」「単に見やすくしてるんでしょうかね」「C拡張がリークするのは不思議でも何でもないw」「heap_live_slotsか…情報どこかにあるか?」「つ↓」

参考: Understanding Ruby GC through GC.stat

⚓型あり言語からRubyに来た人の心理学

2004年なのですごく古いですが、どうやら有名みたいです。

# 同書き込みより
# こうなりがち
   def date=(val)
     class="keyword">case val
     when Date
       @date = val
     when Time
       @date = Date.new(val.year, val.month, val.day)
     when String
       if val =~ /(\d{4})\s*[-\/\\]\s*(\d{1,2})\s*[-\/\\]\s*(\d{1,2})/
         @date = Date.new($1.to_i,$2.to_i,$3.to_i)
       else
         raise ArgumentError, "Unable to parse #{val} as date"
       end
     when Array
       if val.length == 3
         @date = Date.new(val[0], val[1], val[2])
       end
     else
       raise ArgumentError, "Unable to parse #{val} as date"
     end
   end

そして筆者の考えるダックタイピングの真髄はこうだそうです。

# year、month、dayメソッドに応答するオブジェクトを取る
attr_accessor :date

つっつきボイス: 「クラスチェックしたくてcaseで分岐しちゃうやつねー」「でもActiveSupportとかでこういう分岐、普通に見かけますよw」「どっひゃー」「if val行の正規表現はつらそう…」

参考: 静的型と OO というものははじめから… - Oh, you `re no (fun _ → more) — ここから辿りました

⚓「ゴッドクラス」というアンチパターン

Gobyのst0012さんに教わって知りました。

巷では、OOPでは大量のメソッドを保有する万能クラス (ゴッドクラス) を作ってはいけないらしい。また、1メソッドあたり3行程度であるべきで、長いメソッドを書くのはオブジェクト指向的ではないらしい。そういうコードを書くやつはオブジェクト指向がわかっていないのだそうだ。
Qiita記事より


つっつきボイス: 「↑1メソッドあたり3行!来たなー」「引数縦に並べるとそれだけで3行終わっちゃいます」「自分も5行まで!なんて言ったりするけど基本ジョークジョークw」「20行までなら許して欲しい」「50行は多いかな?」「Rubocopのデフォルトって何行まで許されるんでしたっけ?」「コメント除いて10行みたいです」

【保存版】Rubyスタイルガイド(日本語・解説付き)総もくじ

⚓🌟Code Galaxies Visualization🌟


同サイトより

Ruby、もちろんあります。美しすぎる。Go Conference 2018で知りました。


つっつきボイス: 「HomebrewやDebianパッケージまであるし」



同サイトより

宇宙船操作マニュアルとか気分出ますね(Hキーで表示できます)。
今週の🌟を贈呈します。おめでとうございます。

⚓rdl: Rubyで型チェック

# 同リポジトリより
file.rb:
  require 'rdl'
  extend RDL::Annotate

  type '(Integer) -> Integer', typecheck: :now
  def id(x)
    "forty-two"
  end

つっつきボイス:->こういう型宣言する関数型言語、あった気がする」「Ruby 3.0だとコメントに記述する形(アノテーション)で型チェックが入りそうなんでしたっけ」「dry-validationみたいなライブラリレベルの型チェックでいい気がする…」

# dry-rb.orgより
schema = Dry::Validation.Form do
  required(:name).filled
  required(:age).filled(:int?, gt?: 18)
end

schema.("name" => "Jane", "age" => "30").to_h
# => {name: "Jane", age: 30}

schema.("name" => "Jane", "age" => "17").messages
# => {:age=>["must be greater than 18"]}

Rails: dry-rbでForm Objectを作る(翻訳)

⚓その他記事など


つっつきボイス:redoって確かYARVがらみで見かけた気が」

追伸: いわゆるJUMP命令ってパイプラインはstallするけど1 instructionなのでそりゃ早いみたいな話でした(morimorihoge)。

参考: YARV: Yet another RubyVM / Instruction Table

⚓SQL

⚓PostgreSQLで大規模データを処理する(Postgres Weeklyより)


つっつきボイス: 「まだ中身読んでなかった」「ふむふむ、parallelismにパーティショニングにVACUUM FREEZEにFDW…、普通にぽすぐれででかいデータを扱う記事ですね」「(ぽすぐれで計算処理やらせるのかとおもた…)」

⚓PostgreSQL 11に入る「Covering Index」

-- 同記事より
=# CREATE TABLE new_example (a int, b int, c int);
CREATE TABLE
=# INSERT INTO new_example
     SELECT 3 * val, 3 * val + 1, 3 * val + 2
     FROM generate_series(0, 1000000) as val;
INSERT 0 1000001
=# CREATE UNIQUE INDEX new_unique_idx ON new_example(a, b)
     INCLUDE (c);
CREATE INDEX
=# VACUUM ANALYZE;
VACUUM
=#  EXPLAIN ANALYZE SELECT a, b, c FROM new_example WHERE a < 10000;
                       QUERY PLAN
-----------------------------------------------------
 Index Only Scan using new_unique_idx on new_example
     (cost=0.42..116.06 rows=3408 width=12)
     (actual time=0.085..2.348 rows=3334 loops=1)
   Index Cond: (a < 10000)
   Heap Fetches: 0
 Planning Time: 1.851 ms
 Execution Time: 2.840 ms
(5 rows)

つっつきボイス: 「え?Covering Indexってぽすぐれになかった?: MySQLにはずうっと前からあるのに」「ほんとだ、ググるとMySQLばっかり出てくる」

参考: インデックスのみのスキャン: テーブルアクセスを避ける

インデックスのはたらきによって、テーブルアクセスしなくても良かった場合のことを、 カバリングインデックスと言います。これは、インデックスの機能であるかのような響きがあるので、誤解されやすい用語です。インデックスのみのスキャン、という言い方の 方が、実行計画の操作であることを明白に示しています。
上記ブログより

つっつきでMERGE JOINの話にもなったのですが展開思い出せず…

⚓EdgeDBって何じゃろう

Rich Harrisさんって有名な人みたい。

⚓JavaScript

⚓async/awaitヘルからの脱出(JavaScript Weeklyより)

// 同記事より
async function selectPizza() {
  const pizzaData = await getPizzaData()    // async call
  const chosenPizza = choosePizza()    // sync call
  await addPizzaToCart(chosenPizza)    // async call
}

async function selectDrink() {
  const drinkData = await getDrinkData()    // async call
  const chosenDrink = chooseDrink()    // sync call
  await addDrinkToCart(chosenDrink)    // async call
}

(async () => {
  const pizzaPromise = selectPizza()
  const drinkPromise = selectDrink()
  await pizzaPromise
  await drinkPromise
  orderItems()    // async call
})()

// Although I prefer it this way 

(async () => {
  Promise.all([selectPizza(), selectDrink()]).then(orderItems)   // async call
})()

⚓TypeScriptの記法を理解する(JavaScript Weeklyより)

interface Array<T> {
  concat(...items: Array<T[] | T>): T[];
  reduce<U>(
    callback: (state: U, element: T, index: number, array: T[]) => U,
    firstState?: U): U;
  ···
}

つっつきボイス: 「TypeScriptはいいと思う: ただねー、環境の準備がめんどいw」「誰か準備してくれるならTypeScriptにしたい」「そういえばこの中にひとり、TypeScript勢いませんでしたっけ?」「ワイ?| ゜Θ゜)<そうでもないよ」

参考: TypeScript - JavaScript that scales.


typescriptlang.orgより

⚓これで全部終わらそうかという勢いのオンラインドキュメント: Front-End Developer Handbook 2018(JavaScript Weeklyより)


frontendmasters.comより

JS/CSS/HTML…と、新しいフロントエンド絡みのドキュメントを親の仇のように集約しています。


つっつきボイス: 「先週のrubyreferences.github.ioといい、このフォーマットのドキュメントテンプレ流行ってるのかなと思って」「というよりみんなGitHub Pages使ってるからじゃないすか?」「あそっか」「ともあれ、どこまできちんと書かれているかはちゃんと読むまでわからないですけどねw」

追伸: フォーマットの話はGitBookの方ですね。 github.io ドメイン使ってる場合はGitHub Pages連動もしてるでしょうが(morimorihoge)。


frontendmasters.comより

「お、とってもいい地図見つけた↑」「サイトの作りというか、フロントエンドはこれをこういう順に学べという道案内ですね」「同じページの『↓これは学ぶなリスト』にCoffeeScriptが…😢」「SASSも!?」

A short word of advice on learning. Learn the actual underlying technologies, before learning abstractions. Don’t learn jQuery, learn the DOM. Don’t learn SASS, learn CSS. Don’t learn HAML, learn HTML. Don’t learn CoffeeScript, learn JavaScript. Don’t learn Handlebars, learn JavaScript ES6 templates. Don’t just use Bootstrap, learn UI patterns.

「SASSじゃなくて素のCSSにしろと言われてもなー」「PostCSSあたりはどうだろう?」


github.com/postcssより

こちらにも星あげたい気持ちしてきました。

追伸: 落ち着いて読み直してみたら、基礎を学んでから応用のCoffeeScriptなりを学べという文脈でした。

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

⚓Unicodeコンソーシアムの絵文字募集フォーム


unicode.orgより

Turing Complete FMの文字コードの回でも、絵文字の寿司にバリアントがいっぱいあるのにピザがそうじゃないのはどうよとか楽しい話があった気がします。


つっつきボイス: 「次は2020年に採択されるそうです」「文字だけのリストだとわかりにくいなー」「TechRachoのシンボルマークしれっと出したら案外通ったりして?」「(爆)」

Rubyの内部文字コードはUTF-8ではない…だと…?!

⚓出会えてうれしいCSSベスト3

Rubyのメソッドなんかでもこういうのやってみようかな。


つっつきボイス: 「flexboxが人気」「calcも多いですね」

⚓Service Workerとは


同記事より

⚓Go言語

⚓grapi: gRPC APIサーバー&ジェネレーター


つっつきボイス: 「Go Conference 2018でgRPCの登場率高かったです」「gRPCのようなミドルウェア的なものはGoが得意そうっすね」

⚓Go言語がWebAssemblyをサポートへ

Gobyちゃんがブラウザでするっと動く日が来るかなと思って。ちなみにこの記事の情報源は基本的に#18892でした。

⚓Goで学ぶTCP/IPスタック


つっつきボイス: 「そうそう、Goに限らずTCP/IPスタックのコードは一度は読んでおくと勉強になりますヨ」

// 同リポジトリより
// GetTCPProbe returns the TCPProbeFunc if installed with AddTCPProbe, nil
// otherwise.
func (s *Stack) GetTCPProbe() TCPProbeFunc {
    s.mu.Lock()
    p := s.tcpProbeFunc
    s.mu.Unlock()
    return p
}

// RemoveTCPProbe removes an installed TCP probe.
//
// NOTE: This only ensures that endpoints created after this call do not
// have a probe attached. Endpoints already created will continue to invoke
// TCP probe.
func (s *Stack) RemoveTCPProbe() {
    s.mu.Lock()
    s.tcpProbeFunc = nil
    s.mu.Unlock()
}

⚓Go格言集とGo Conference 2018

先週紛れ込んだGo Conference 2018で知りました。元記事には詳しい説明へのリンクがあります。雑に訳してみました。


  • メモリ共有で通信するのも、通信でメモリ共有するのもまかりならぬ
  • コンカレンシーは並列(parallelism)にあらず
  • チャンネルは協調動作(オーケストレーション)のためのもの、ミューテックスはシリアライズのためのもの
  • インターフェイスが肥大化するほど抽象化の度合いが落ちる
  • ゼロ値を有用なものにせよ
  • interface{}それ自身は何の情報ももたらさない
  • gofmtのスタイルを好きなやつなど誰もいない、だからこそgofmtは好かれる
  • 依存をわずかでもこしらえるくらいなら、コピーしてしまえ
  • syscallは常にビルドタグで保護しなければならぬ
  • cgoは常にビルドタグで保護しなければならぬ
  • cgoはGoにあらず
  • 安全でないパッケージを使ったが最後、何の保証もありはしない
  • 巧みなコードを書くより、明確なコードを書くがよい
  • リフレクションが明確になることなどありえぬ
  • エラーとは値のことであり、それ以外の何ものでもない
  • エラーをチェックするだけでは足りぬ、丁重に処理せよ
  • アーキテクチャを設計し、コンポーネントに命名し、詳細をドキュメントに書け
  • ドキュメンテーションはユーザーのためのものである
  • うろたえるな

こういうのって言語の思想を手っ取り早く知るのに便利ですね。

つっつきボイス: 「Goの格言、( ・∀・)イイ!!こういうの大事だし、”Concurrency is not parallelism.”とかごもっとも」

「ところでこのページのGopherくんの絵、このユルさがもうたまらないw」「この雑なライトセーバーみたいなやつ↓、測ってないけど45度としか思えないw」


go-proverbs.github.ioより


Go Conference 2018をすごーく手短にまとめると、以下が印象に残っています。多すぎてここには到底盛り込めないので。

  • Goのmap(Rubyで言うハッシュ)は本質的にスレッドセーフにならないし、しない
  • Go界隈ではマイクロサービスの普及がだいぶ進んでいる(少なくともRails Developers Meetupでの挙手数と比べれば)
  • 一方モノリスのよさも見直されつつあったりするらしい: GCPといえども下手にサービスを割るとオーバーヘッドにつながるとか
  • Goは公式資料がエクストリームに充実しているので、野良ブログより先にこちらを最初にあさるべし
  • GoでORMするならActiveRecordのことは忘れること — 「ActiveRecordの完成度はやっぱり凄い」という率直な感想も

参考: Go Conference 2018 Spring - 資料一覧 - connpass

開発チームを苦しめるマイクロサービス(翻訳)

⚓その他

⚓同人誌「現代Webフロントエンドデザインパターン」


つっつきボイス: 「BPSのアプリチームには技術書典の常連がいるらしいという噂」「どかっと積み上げてくれるのかな( ^ω^)ワクワク」

⚓大物の独自路線: Microsoftの独自LinuxカーネルとAmazonの独自ブラウザ


つっつきボイス: 「あーこれねw」「Amazonの独自ブラウザの名前がInternetって…」「誰に挑戦してるんだろうか」

参考: Amazonが容量わずか2MBの軽量版インターネットブラウザ、その名も「Internet」をリリース - GIGAZINE

⚓GCP BigQueryの東京リージョン開始


つっつきボイス: 「お、今まで東京リージョンなかったのか」

クラウド業界でAWSのサービス名の命名センスが他にも広がってるような気が個人的にしています。

⚓その他のその他



⚓番外

⚓英国BBCのGitHubリポジトリ


つっつきボイス: 「仕事中にたまたま見つけてくれた人がいたので」「本物なんですね」「BBCはこういうのをけっこうちゃんとやってますね」

↓動画は直接BBCとは関係ありませんが。

⚓手計算だったし

⚓そこまで精度上がったか

ビルの窓ガラスの振動を外からレーザー光線で測定して音声を盗聴するという技術を思い出しました。


つっつきボイス: 「おお、この手の技術は実は結構前からあるんですよ」「PLCって?」「まさに電力線搬送通信(Power Line Communication)で、屋内の電源コードを使ってデータ通信する技術」「へー!」「ちなみにPLCやるときはデータをPLC経由では屋外に出せないんですよ」「出せないっていうのは、法律的に?」「そう: 厳しく制限されている」「そっかー、日本は電波法厳しいし」

参考: 電力線搬送通信 - Wikipedia
参考: 電波法 - Wikipedia

⚓複合原子層作製システム


iis.u-tokyo.ac.jpより

参考: 東大、シート状の原子層を自在に積み重ねるロボットシステムを開発 | マイナビニュース

⚓日本沿岸で全世界数百年分のレアメタル鉱床が見つかる


つっつきボイス: 「どうやって掘り出すかが問題ですからねー」「何しろ海の底なんで」


今週は以上です。

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

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

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

Rails公式ニュース

Ruby Weekly

RubyFlow

160928_1638_XvIP4h

Postgres Weekly

postgres_weekly_banner

Frontend Focus

frontendfocus_banner_captured

JavaScript Weekly

javascriptweekly_logo_captured

Vue.jsサンプルコード(38)入力をやめるとリダイレクトする

$
0
0

38. 入力をやめるとリダイレクトする

  • Vue.jsバージョン: 2.5.2
  • フィールドに何か入力されている間はリダイレクトされません。
  • 入力をやめると、1秒後に元のページにリダイレクトします。
    • 以下のJSFiddleでは、何も入力しないか入力を止めると、フレーム内のみ元のページにリダイレクトします。
    • ブラウザによってはこのページの直前のページにリダイレクトする可能性もあります。
  • 画面をリロードすると最初に戻ります。

サンプルコード

前回の「一定時間経過後に自動でリダイレクトする」とほとんど変わりません。


バックナンバー(Vue.jsサンプルコード)

Vue.jsサンプルコード(01〜03)Hello World・簡単な導入方法・デバッグ・結果の表示とメモ化

【保存版】Rails 5 Webpacker公式ドキュメントの歩き方+追加情報

$
0
0

こんにちは、hachi8833です。

Rails 5のWebpackerでつらみを踏まないために、公式のWebpackerドキュメントの要点をまとめてみました。追加情報がありましたら今後も更新したいと思います。


rails/webpackerより

公式のドキュメントが分散しているのと、mdファイル名から内容が推測しにくくて見通しがよくないので、ファイルの並びを再編成して概要を一覧できるようにしました(せめてファイル名の頭に番号でも振ってくれれば…)。具体的な設定方法についてはリンク先をご覧ください。

⚓概要

  • ドキュメントリポジトリ: rails/webpacker/tree/master/docs — mdが16ファイルあります。本記事ではこちらのみを扱います。
  • 上のドキュメントリポジトリの最終更新日: 2018/04/23
    • 更新や誤りにお気づきの方は@hachi8833までお知らせください。
  • 参考: README rails/webpacker — こちらが網羅的ですが、ドキュメントと重複している部分もあるようです。本記事ではREADMEは対象に含めていません。
  • 参考: Webpack公式ガイド

⚓Webpackerのインストール方法

Rails 5のWebpackerは以下のいずれかの方法で導入できます。

  • 新規の場合: rails new アプリ名 --webpack
  • 後からの場合: Gemfileでgem 'webpacker'を有効にして、bin/rails webpacker:installを実行

⚓Webpacker公式ドキュメントの一覧

以下、重要な順にリストアップします。

⚓1. 最初に読むもの

  • yarn.mdyarnで nodeのモジュールを追加する方法です。ほんのちょっぴりです。
  • webpack.md — Webpackの公式な設定方法が記載されています。重要度高し(後述)。
  • folder-structure.md — Webpackerを使った場合のフォルダ構造や名前空間の切り方について説明されています。
  • env.md — オプションで設定できるWebpacker環境変数について説明されています。開発環境でForemanInvokerを併用する場合の注意点についても書かれてます。

⚓2. 次ぐらいに読むもの

  • assets.md — Railsビューから静的アセットにリンクする方法、静的アセットをNodeモジュールからインポートする方法、ヘルパー経由でSprocketsから静的アセットをインポートする方法、Babelのモジュールリゾルバで静的アセットを直接参照する方法について説明されています。
  • css.md — CSS/SaSS/SCSSをJavaScriptファイルにグローバルまたはスコープ指定で直接インポートする方法などについて説明されています。Railsビューでのリンク方法、Bootstrapの追加方法、CSSのpost-processライブラリの読み込み方法、vue-loaderによるCSS読み込み、url-loaderによるCSS読み込み方法についても記載されています。

⚓3. その次ぐらいに読むもの

  • webpack-dev-server.mdwebpack-dev-serverで開発環境向けのローカルWebサーバーを立ててファイル更新を自動で反映する方法について説明されています(追記参照↓)。
    • misc.md — vimやemacsを利用中に特定のファイルを自動反映の対象外にする方法について説明されています。
  • testing.md — Capybaraの設定方法や、TypeScript向けにKarmaを設定する方法について説明されています。
  • deployment.md — Heroku/Nginx/CDNでのデプロイの注意点について簡単に説明されています。Sprocketsを使わない場合の方法についても記載されています。
  • troubleshooting.md — 文字どおりのトラブルシューティング集です。

追記(2018/05/18): 上で言及されているwebpack/webpack-dev-serverは現在メンテナンスモードに移行済みです。現在はdevなしのwebpack-contrib/webpack-serveに移行しましたが、現時点ではRailsのWebpackerリポジトリにまだ導入されていない様子です。新名称の末尾はserverではなくserveなので微妙に紛らわしいです。
morimorihogeさん、情報ありがとうございました!🙇

⚓4. 参考に読むもの

  • docker.md — Docker化されたRailsアプリでWebpackerを使う方法について説明されています。簡単だと言ってます。
  • cloud9.md — devサーバーをAWSのCloud9環境で使う方法について説明されています。
  • es6.md — Webpackerに同梱されているBabel経由で利用できるECMA6の機能や、require()module.exportsではなくimportexportを使うべきという注意事項が記載されています。
  • typescript.md — TypeScriptをReact.jsやVue.jsで利用する方法や、HTMLテンプレートとAngularで利用する方法について説明されています。
  • props.md — React.jsやVue.jsやElm.jsでのpropsの使い方について説明されています。

⚓webpack.mdの要点

webpack.mdは重要度が高いので、以下に要点をまとめます。

⚓1. 設定

  • development/test/production環境向けの設定はconfig/webpack/*.jsに保存される
    • デフォルトのconfig/webpack/*.jsはproductionで使える標準設定になっているので、基本的に変更は不要
    • 必要な場合はconfig/webpack/environment.jsを参考にそれぞれの環境向けに設定するとよい。

注意: 公式の設定サンプルでやっているようにaliasで指定すること。

// config/webpack/custom.js
module.exports = {
  resolve: {
    alias: {
      jquery: 'jquery/src/jquery',
      vue: 'vue/dist/vue.js',
      React: 'react',
      ReactDOM: 'react-dom',
      vue_resource: 'vue-resource/dist/vue-resource',
    }
  }
}

⚓2. ローダー

  • Webpackerが持つ以外のローダーもyarnで環境に追加できる。後はドキュメントのとおりに設定。
yarn add json-loader
  • Webpackerが持つローダーの設定をカスタマイズすることも可能。以下はbabelをカスタマイズする場合。
// config/webpack/environment.js
const { environment } = require('@rails/webpacker')

const babelLoader = environment.loaders.get('babel')
babelLoader.options.cacheDirectory = false

module.exports = environment

a. CoffeeScriptの場合

  • WebpackerではCoffeeScript 1がデフォルトで利用できる。CoffeeScript 2を使いたいのであればyarn add coffeescript@2.0.1で追加してからドキュメントに従って設定する。

b. React SVG loaderの場合

  • React SVG loaderを使う場合、SVGローダーはファイルローダーより前に読み込むこと。

c. URL loaderの場合

(設定方法のみ)

d. Webpacker 3以降でローダーオプションをオーバーライドする方法

  • ローダーにオプションを足したり変更したりしたい場合は、config/webpack/environment.jsを編集してオーバーライド用のオブジェクトを用意すること。
    • オーバーライド方法については#756も参照
    • 以下はCSS Modulesをオーバーライドする場合
const { environment } = require('@rails/webpacker')
const merge = require('webpack-merge')

const myCssLoaderOptions = {
  modules: true,
  sourceMap: true,
  localIdentName: '[name]__[local]___[hash:base64:5]'
}

const CSSLoader = environment.loaders.get('sass').use.find(el => el.loader === 'css-loader')

CSSLoader.options = merge(CSSLoader.options, myCssLoaderOptions)

module.exports = environment
  • ビューにstylesheet_pack_tagを置かないと効かないことに注意。
<%= stylesheet_pack_tag 'YOUR_PACK_NAME_HERE' %>

⚓3. プラグイン

  • Webpackプラグインの追加/変更方法は上述のローダーと同じ手順でできる。

⚓4. モジュールの解決

  • resolve.modulesへのパスを追加するAPIは、ローダーやプラグインの場合と同じ。
const { environment } = require('@rails/webpacker')

// Resolved modules list API - prepend, append, insert
environment.resolvedModules.append('vendor', 'vendor')

a. CommonsChunkPlugin

  • CommonsChunkPluginを使うと、複数のエントリポイントで使われる共通モジュールを含む個別のファイル(チャンク: chunk)を作成できるようになる。
    • 共通モジュールをバンドルから切り離すことで、生成されたチャンクファイルが最初に一括読み込みされ、キャッシュに保存されて後で利用できるようになる。
    • 共通コードがキャッシュから読み出されるのでブラウザ表示が高速化される。
    • 新しいページを開くたびに巨大なバンドルを毎度強制読み込みしなくてよくなる。
  • 設定方法はドキュメントを参照

関連記事

新しいRailsフロントエンド開発(1)Asset PipelineからWebpackへ(翻訳)

RailsのVue.jsをWebpackerとJestでテストする(翻訳)

Vue.jsサンプルコード(39)画像の配置や背景をスライドバーで簡単に調整する

$
0
0

40. 画像の配置や背景をスライドバーで簡単に調整する

  • Vue.jsバージョン: 2.5.2
  • 画面をリロードすると最初に戻ります。

サンプルコード

ポイント

inputに割り当てた変数からtransformの書式を作るだけでできます。

  const vm = new Vue({
    el: "#app",
    data: {s: 1.0, x: 0, y: 0, a: 0, c: "#ADFF2F", b: 64},
    computed: {
      transform: function() { return `scale(${this.s}) translate(${this.x}px, ${this.y}px) rotate(${this.a}deg)` },
    },
  })

バックナンバー(Vue.jsサンプルコード)

Vue.jsサンプルコード(01〜03)Hello World・簡単な導入方法・デバッグ・結果の表示とメモ化

Rails 5: Webpacker公式README — Webpack v4対応版(翻訳)

$
0
0

本READMEの他に、以下のWebpacker公式ドキュメントの歩き方記事もどうぞ。これら2本で、Webpackerの公式ドキュメントは概観できるかと思います。次は公式のユースケースが欲しいです🍰🍵

【保存版】Rails 5 Webpacker公式ドキュメントの歩き方+追加情報

概要

MITライセンスに基いて翻訳・公開いたします。

更新や誤りにお気づきの方は@hachi8833までお知らせください。

Rails 5: Webpacker公式README — Webpack v4対応版(翻訳)

Webpackerを用いることで、JavaScriptのプリプロセッサ兼バンドラーであるwebpack 4.x.x+を簡単に使えるようになり、アプリ的なJavaScriptをRailsで管理できます。Webpackerはアセットパイプラインと共存します。その理由は、Webpackがアプリ的なJavaScriptを主要な対象としているためです。app/assetsの下に居座り続ける画像やCSS、ましてやJavaScript Sprinklesは、Webpackの主要な対象ではありません。

しかし(訳注: Webpackと異なり)WebpackerはCSS/画像/フォントアセットも同様に扱うことが可能なので、アセットパイプラインすら不要になるかもしれません。Webpackerは、コンポーネントベースのJavaScriptフレームワークを独占的に利用する場合に最も適しています。

メモ: masterブランチのコードがついに(訳注: Webpack)v4.x.xになりました。3.x系ドキュメントについては3-x-stableブランチをご覧ください。

⚓必要な環境

  • Ruby: 2.2以降
  • Rails: 4.2以降
  • Node.js: 6.0.0以降
  • Yarn: 0.25.2以降

⚓Webpackerで利用できる機能

  • webpack 4.x.x
  • ES6(ES2015) — babel経由
  • マルチエントリポイントを用いた自動コード分割
  • スタイルシート: SassとCSS
  • 画像とフォント
  • PostCSS – Auto-Prefixer
  • アセット圧縮、ソースマップ、最小化(minification)
  • CDNサポート
  • React、Angular、Elm、Vueをすぐに利用可能
  • Railsビューヘルパー
  • 拡張および設定可能

⚓インストール方法

Rails 5.1以降のアプリを新規作成するときに--webpackという新オプションでWebpackerを追加できます。

# Rails 5.1以降で利用可能
rails new myapp --webpack

あるいはGemfileに以下のように追加して、

# Gemfile 
gem 'webpacker', '~> 3.5'

# masterを使いたい場合
gem 'webpacker', git: 'https://github.com/rails/webpacker.git'

# 4.x pre-releaseを試してみたい場合
gem 'webpacker', '>= 4.0.x'
yarn add @rails/webpacker@4.0.0-pre.2  # 訳注: この行はコマンドで実行

最後に以下を実行してWebpackerをインストールすることもできます。

bundle
bundle exec rails webpacker:install

# Rails 5.0 より前の場合
bundle exec rake webpacker:install

⚓使い方

インストールが終われば、現代的なES6フレーバーJavaScriptアプリをすぐにでも書き始められます。

app/javascript:
  ├── packs:
  │   # ここにはwebpackエントリファイルだけが置かれる
  │   └── application.js
  └── src:
  │   └── application.css
  └── images:
      └── logo.svg

javascript_pack_tagヘルパーを用いて、Railsビュー内でJavaScriptのpackをリンクできます。packファイルでスタイルシートがインポートされる場合は、stylesheet_pack_tagでリンクできます。

<%= javascript_pack_tag 'application' %>
<%= stylesheet_pack_tag 'application' %>

<link rel="prefetch">タグや<img />タグで静的アセットをリンクしたい場合は、asset_pack_pathヘルパーを利用できます。

<link rel="prefetch" href="<%= asset_pack_path 'application.css' %>" />
<img src="<%= asset_pack_path 'images/logo.svg' %>" />

メモ: スタイルシートや静的アセットのファイルをビューから利用するには、それらをパックやエントリファイルにリンクする必要があるでしょう。

⚓開発

Webpackerをインストールすると、./bin/webpack./bin/webpack-dev-serverという2つのbinstubができます。どちらも、それぞれ標準のwebpack.js実行ファイルとwebpack-dev-server.js実行ファイルの薄いラッパーであり、環境に合う設定ファイルや環境変数を正しく読み込むためのものです。

訳注(2018/05/24): 本READMEで言及されているwebpack/webpack-dev-serverは現在メンテナンスモードに移行済みです。現在はdevなしのwebpack-contrib/webpack-serveに移行しましたが、現時点ではRailsのWebpackerリポジトリにまだ導入されていない様子です。新名称の末尾はserverではなくserveなので微妙に紛らわしいです。

development環境のWebpackerは、デフォルトでは(一括ではなく)必要に応じてコンパイルを実行します。コンパイルは、Webpackerのヘルパーメソッドを用いてpackアセットを参照するときに実行されます。つまり、コンパイルのために別プロセスを走らせる必要がないということです。コンパイルエラーは標準のRailsログに出力されます。

コードのライブリロード機能を使いたい場合や、JavaScriptの量が多すぎてオンデマンドコンパイルが遅い場合は、./bin/webpack-dev-serverまたはruby ./bin/webpack-dev-serverを実行する必要があります。Windowsユーザーは、bundle exec rails sを実行しているターミナルとは別のターミナルでがこれらのコマンドを実行する必要があります。このプロセスはapp/javascript/packs/*.jsファイルの変更を監視して自動的にブラウザ画面を再読み込みして最新表示にします。

# webpack dev server
./bin/webpack-dev-server

# watcher
./bin/webpack --watch --colors --progress

# スタンドアロンでのビルド
./bin/webpack

development用サーバーが起動すると、Webpackerは自動的にすべてのwebpackアセットへのリクエストをこのサーバーにプロキシし始めます。サーバーを停止すると、元のオンデマンドコンパイルに復帰します。

webpack-dev-serverでサポートされている環境変数をWEBPACKER_DEV_SERVER_<オプション名>の形式で利用できます。この環境変数の設定は、設定ファイルよりも常に優先される点と、同じ環境変数をrails serverからも利用できるようにしておかなければならない点にご注意ください。

WEBPACKER_DEV_SERVER_HOST=example.com WEBPACKER_DEV_SERVER_INLINE=true WEBPACKER_DEV_SERVER_HOT=false ./bin/webpack-dev-server

development環境におけるwebpack-dev-serverは、セキュリティ上の理由によりデフォルトでlocalhostをリッスンします。しかし、アプリをローカルLANのIPやVagrantなどのVMインスタンスからアクセス可能にしたい場合は、./bin/webpack-dev-server binstubの実行時に次のようにhostを設定できます。

WEBPACKER_DEV_SERVER_HOST=0.0.0.0 ./bin/webpack-dev-server

メモ: アプリを制限CSP環境(Rails 5.2以降など)で実行する場合は、webpack-dev-serverホストをconnect-srcの「allowed origin」として許可する必要があります。この設定は、Rails 5.2以降のCSPイニシャライザconfig/initializers/content_security_policy.rbに以下のようなスニペットを用いて行えます。

policy.connect_src :self, :https, 'http://localhost:3035', 'ws://localhost:3035' if Rails.env.development?

メモ: Windows環境でこれらのbinstubを実行する場合はrubyをプレフィックスとして追加するのを忘れないように。

⚓Webpackの設定方法

Webpackの設定方法やローダーの変更方法については、docs/webpack.mdをご覧ください。

訳注: 以下記事の「最初に読むもの」もどうぞ。

【保存版】Rails 5 Webpacker公式ドキュメントの歩き方+追加情報

⚓Rails環境のカスタマイズ

Webpackerのconfig/webpacker.ymlは、最初からdevelopment/test/production環境向けに利用できる状態になっています。しかしほとんどのproductionアプリでは開発ワークフローの一端として他の環境も必要になります。Webpacker 3.4.0以降、追加環境についてもすぐ利用できるようになっています。

webpacker.ymlファイルで追加の環境設定を次のように定義できます。

staging:
  <<: *default

  # productionはパフォーマンス向上のためプリコンパイル後の起動に依存
  compile: false

  # パフォーマンス向上のためのmanifest.jsonキャッシュ
  cache_manifest: true

  # stagingのpackを別ディレクトリにコンパイル
  public_output_path: packs-staging

それ以外の場合、Webpackerの設定読み込みはproduction環境にフォールバックします。NODE_ENVに設定できるのはproductionまたはdevelopmentである点にご注意ください。つまり、config/webpacker/*の下に追加の環境ファイルを作成する必要はありません。代わりに、RAILS_ENVを用いて別の設定をwebpacker.ymlで読み込んでください。

たとえば、以下のコマンドを実行するとアセットがproductionモードでコンパイルされますが、config/webpacker.ymlで(staging環境設定が)利用できる場合はそのstaging設定を読み込み、そうでなければproduction環境設定にフォールバックします。

RAILS_ENV=staging bundle exec rails assets:precompile

以下のコマンドを実行すると、developmentモードでコンパイルし、webpacker.ymlにcucumber環境の設定が定義されていればそれを読み込み、なければproduction設定にフォールバックします。

RAILS_ENV=cucumber NODE_ENV=development bundle exec rails assets:precompile

binstubでのコンパイルはdevelopmentモードで行われますが、rakeタスクでのコンパイルはproductionモードで行われる点にご注意ください。

# developmentモードでコンパイル(NODE_ENVが指定されていない場合)
./bin/webpack
./bin/webpack-dev-server

# デフォルトでproductionモードでコンパイル(NODE_ENVが指定されていない場合)
bundle exec rails assets:precompile
bundle exec rails webpacker:compile

⚓アップグレード

Webpackerは、以下のコマンドを実行することで最新の安定版にアップグレードされます。このプロセスには、gemや関連するJavaScriptパッケージのアップグレードも含まれます。

bundle update webpacker
rails webpacker:binstubs
yarn upgrade @rails/webpacker --latest
yarn add webpack-dev-server@^2.11.1

# (プレリリースを含む)最新リリースをインストールする場合
yarn add @rails/webpacker@4.0.0-pre.2

⚓Yarn統合

development環境のWebpackerは、デフォルトでyarn統合チェックを実行してローカルのJavaScriptパッケージをすべて最新にします。これはRailsで用いられるbundlerと似ていますが、対象がJavaScriptである点が異なります。システムが古い場合、Railsは初期化を行いません。その場合yarn installを実行してローカルJavaScriptパッケージをアップグレードするよう求められます。

このオプションをオフにするには、Railsのdevelopment環境の設定ファイル(config/environment/development.rb)に新しく設定オプションを追加することでデフォルトの動作をオーバーライドする必要があります。

config.webpacker.check_yarn_integrity = false

Railsのどの環境設定ファイルについても、以下の設定オプションを追加することでこの機能をオンにできます。

config.webpacker.check_yarn_integrity = true

⚓統合

Webpackerは、React、Angular、Vue、Elmとの基本的な統合をすぐに利用できるようになっています。
利用可能なコマンドやタスクの一覧は、bundle exec rails webpackerで表示できます。

⚓Reactを使う場合

WebpackerでReactを使うには、Rails 5.1以降のアプリをrails newするときに--webpack=reactオプションを追加します。

# Rails 5.1以降
rails new myapp --webpack=react

(Webpackerが設定済みの既存Railsアプリの場合はbundle exec rails webpacker:install:reactを実行します)

インストーラはyarmを用いて関連する依存関係を追加し、設定ファイルを変更して、サンプルのReactコンポーネントをapp/javascript/packsに追加します。これを用いてすぐにでもReactを試すことができます。

⚓AngularでTypeScriptを利用する場合

WebpackerでAngularを使うには、Rails 5.1以降のアプリをrails newするときに--webpack=angularを追加します。

# Rails 5.1以降
rails new myapp --webpack=angular

(Webpackerが設定済みの既存Railsアプリの場合はbundle exec rails webpacker:install:angularを実行します)

インストーラはyarnを用いてTypeScriptとAngularのコアライブラリを追加し、それに応じて設定ファイルでいくつかの変更を行います。TypeScriptで記述されたサンプルコンポーネントもapp/javascriptに追加されますので、これを用いてすぐにでもAngularを試すことができます。

development環境でのAngularはデフォルトでJITコンパイラを用います。このコンパイラは、Rails 5.2以降のような制約CSP(Content Security Policy)環境と互換性がありません@ngtools/webpackプラグインを用いて Angular AOTをdevelopment環境で使う手もあります。

または、Rails 5.2以降を利用しているのであればdevelopment環境でunsafe-evalを有効にする方法もあります。有効にするには、config/initializers/content_security_policy.rbに以下のコードを設定します。

  if Rails.env.development?
    policy.script_src :self, :https, :unsafe_eval
  else
    policy.script_src :self, :https
  end

⚓Vueを使う場合

WebpackerでVueを使うには、Rails 5.1以降のアプリをrails newするときに--webpack=vueオプションを追加します。

# Rails 5.1以降
rails new myapp --webpack=vue

(Webpackerが設定済みの既存Railsアプリの場合はbundle exec rails webpacker:install:vueを実行します)

インストーラはyarnを用いてVueや必要なライブラリを追加し、必要に応じて設定ファイルを自動更新します。サンプルコンポーネントもapp/javascriptに追加されますので、これを用いてすぐにでもVueを試すことができます。

Rails 5.2以降を利用している場合は、development環境でunsafe-evalを有効にする必要があります。有効にするには、config/initializers/content_security_policy.rbに以下のコードを設定します。

  if Rails.env.development?
    policy.script_src :self, :https, :unsafe_eval
  else
    policy.script_src :self, :https
  end

詳しくはVueのドキュメントをご覧ください。

⚓Elmを使う場合

WebpackerでElmを使うには、Rails 5.1以降のアプリをrails newするときに--webpack=elmオプションを追加します。

# Rails 5.1以降
rails new myapp --webpack=elm

(Webpackerが設定済みの既存Railsアプリの場合はbundle exec rails webpacker:install:elmを実行します)

Elmライブラリとコアライブラリのパッケージは、yarnとElmを用いて追加されます。
サンプルMain.elmアプリもapp/javascriptに追加されますので、これを用いてすぐにでもElmを試すことができます。

⚓Stimulusを使う場合

WebpackerでStimulusを使うには、Rails 5.1以降のアプリをrails newするときに--webpack=stimulusオプションを追加します。

# Rails 5.1以降
rails new myapp --webpack=stimulus

(Webpackerが設定済みの既存Railsアプリの場合はbundle exec rails webpacker:install:stimulusを実行します)

The Stimulus Handbookを読むか、stimulusjs/stimulusのソースコードで詳しい内容を学べます。

⚓CoffeeScriptを使う場合

WebpackerにCoffeeScriptサポートを追加するには、Webpackerが設定済みの既存Railsアプリでbundle exec rails webpacker:install:coffeeを実行します。

サンプルhello_coffee.coffeeファイルもapp/javascriptに追加されますので、これを用いてすぐにでもCoffeeScriptを試すことができます。

⚓Erbサポートを使う場合

JSテンプレートにErbサポートを追加するには、Webpackerが設定済みの既存Railsアプリでbundle exec rails webpacker:install:erbを実行します。

サンプルhello_erb.js.erbファイルもapp/javascriptに追加されますので、これを用いてすぐにでもErbフレーバーのJavaScriptを試すことができます。

⚓インストールパスについて

デフォルトのWebpackerは、従来のRailsアプリと同様のシンプルな方法でJavaScriptアプリファイルやコンパイル済みwebpackバンドルを配置します。これらのオプションはすべてconfig/webpacker.ymlファイルで設定できます。

デフォルトのwebpackコンパイル設定は、従来のapp/javascript/packs/*(デフォルト)にあるすべてのファイルや、webpacker.ymlsource_entry_pathで設定した任意のパスを対象とし、それぞれ独自の出力ファイル(webpackが呼び出す場合はエントリポイントも)に変換されます。これによって、エントリポイントにしたくないものをpacksディレクトリ以下に配置する必要がなくなります。簡単に言うと、ビューでリンクしたいファイルはすべてpacksディレクトリ内に配置し、それ以外のものはすべてapp/javascriptに配置してください。

たとえばソースディレクトリをapp/javascriptからfrontendに変更し、出力先をassets/packsにしたいのであれば、次のように書けばよいでしょう。

# config/webpacker.yml
source_path: frontend
source_entry_path: packs
public_output_path: assets/packs # 出力先 => public/assets/packs

同様に、webpack-dev-server設定を変更するにはconfig/webpacker.yml`で次のようにできます。

# config/webpacker.yml development:
  dev_server:
    host: localhost
    port: 3035

スタイルシートにJavaScriptコードを含めてホットリロードされるように設定したいのであれば、hmrtrueに変更すればstylesheet_pack_tagからは何も出力されなくなります。production環境やtest環境でのstylesheet_pack_tagは適切なHTMLタグを生成します。

⚓resolved_pathsオプション

Webpackerを追加する既存のアプリでほとんどのアセットがapp/assetsの下やエンジン内に保存されていて、かつそれらをwebpackモジュールで共有したい場合は、config/webpacker.ymlファイルのresolved_pathsを利用できます。これを用いて、webpackがモジュールを解決するときに探索すべきパスを追加できます。

resolved_paths: ['app/assets']

続いて、モジュール内で次のようにインポートします。

// 親ディレクトリ(app/assetsなど)からの相対パスである点に注意
import 'stylesheets/main'
import 'images/rails.png'

注意: パスの追加は慎重に行ってください。さもないとコンパイル速度が低下します。参照の必要なモジュールが1〜2個程度であれば、親ディレクトリ全体ではなく、個別のパスを追加することを検討しましょう。

⚓watched_paths配列

デフォルトでは、遅延コンパイル結果は監視対象パスの下にあるファイルが変更されるまでキャッシュされます。watched_paths配列に新しいパスを追加することで監視対象を設定できます。これはRailsのautoload_pathsとよく似ています。

# config/initializers/webpacker.rb
# またはconfig/application.rb
Webpacker::Compiler.watched_paths << 'bower_components'

⚓デプロイ

Webpackerは新しいwebpacker:compileタスクをassets:precompileにフックします(assets:precompileを実行すると必ず実行される)。Sprocketsを使っていない場合、webpacker:compileは自動的にassets:precompileにエイリアスされます。Sprocketsと同様、rakeタスクもproductionモードでpacksをコンパイルしますが、設定の読み込みにはRAILS_ENVを使います(config/webpacker.ymlがある場合はこれを使う)。

⚓ドキュメント

より詳しいガイドはdocsをご覧ください。

訳注: 以下のWebpacker公式ドキュメントの歩き方記事もどうぞ。

【保存版】Rails 5 Webpacker公式ドキュメントの歩き方+追加情報

⚓ライセンス

WebpackerはMIT Licenseに基いてリリースされています。

関連記事

新しいRailsフロントエンド開発(1)Asset PipelineからWebpackへ(翻訳)

RailsのVue.jsをWebpackerとJestでテストする(翻訳)

Rails tips: Railsアプリに1行書くだけでチャートを作成できるchartkick/chartable gem(翻訳)

$
0
0

概要

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

Chartableは著者自身が作成したgemです。

Rails tips: Railsアプリに1行書くだけでチャートを作成できるchartkick/chartable gem(翻訳)

Railsアプリでチャートが使えたらとても素敵ですが、正しいデータを準備してからJavaScriptでビューに表示するのはつらいことがあります。そこで今回はChartkick gemとChartable gemを用いるソリューションをご紹介いたします。

Chartkick gemは、コードを1行書くだけで美麗かつ有用なチャートを表示できます。Chartable gemは、任意のActive Recordクエリを分析可能なハッシュに変換します。この2つのgemを用いることで、チャート作成のための新しい武器が手に入ります。

設定方法

最初にGemfileに以下のgemを追加します。

gem 'chartkick'
gem 'chartable'

bundle installを実行したら、次はチャートのレンダリングに使うJavaScriptライブラリを読み込まなければなりません。これを行うには、application.jsに以下の行を追記します。

//= require Chart.bundle
//= require chartkick

分析データを取得する

Railsで作成したブログエンジンがあり、そこにArticleモデルがあるとします。そして毎月何件の記事が作成されているかを表示したいとしましょう。これを行うにはクエリをひとつ実行しなければなりません。

@articles = Article.analytics(:monthly)

4月に1件、5月に5件の記事が作成されていた場合は、次のようなハッシュが取れるはずです。

{"April 2018" => 1, "May 2018" => 5}

チャートに表示する

それではデータをビューに表示しましょう。最初に申し上げたとおり、必要なのはこの1行だけです。

<%= pie_chart(@articles) %>

これで、以下のチャートが表示されるはずです。

できました!他のチャートに変更したい場合や期間の異なるデータを取得したい場合は、以下のgemページをご覧ください。


最新記事をチェックしたい方はぜひhttps://twitter.com/pdabrowski_k1のフォローをお願いします。github.com/rubyheroに掲載される新しいRuby gemもどうぞお見逃しなく!

お知らせ: RSpec & TDDの電子書籍を無料でダウンロード

もっと稼ぎたい方や会社をさらに発展させたい方へ: テスティングのスキルの重要性にお気づきでしょうか?テストを正しく書き始めることが、唯一のファーストステップです。無料でダウンロードいただける私の書籍『RSpec & Test Driven Developmentの無料ebook』をどうぞお役立てください。

関連記事

Rails: ページタイトルはビューテンプレートの`content_for`で表示すること(翻訳)


Railsのフロントエンドのノウハウ#1: システムテスト編(翻訳)

$
0
0
  • 次記事: Railsのフロントエンドのノウハウ#2: JavaScript編(翻訳)

概要

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

記事を2本に分割しました。日本語タイトルは内容に即したものにしています。

Railsのフロントエンドのノウハウ#1: システムテスト編(翻訳)

記事の長さ: 8分

Rails 5以降の新しい機能のひとつに「システムテスト」があります。システムテストは、Capybaraを用いたフル装備のフロントエンドテスト機能を提供し、通常のユーザーが体験する完璧なWebエクスペリエンスを提供する本物のブラウザウィンドウを用いて各テストを実行します。この機能がRailsに組み込まれたことでテストがエレガントになり、テストに全力で集中できるようになります。

さらにうれしいことに、標準のRailsジェネレータを用いてモデル名や属性フィールドを生成すると、標準のCRUDシステムテストを自動生成してくれるので、フロントエンドフレームワークの差し替え作業が大幅にシームレスになります。

今回の記事では、前回の記事で用いたRails+VueJSアプリでコード変更が生じたときにシステムテストをすべてパスさせてご覧にいれます。その他に、テストで有用なJavaScriptの知識についてもいくつかご紹介いたします。

フロントエンドアプリを更新する

ここでは、前回の「VueJS Components with CoffeeScript for Rails」記事で使ったアプリを変更します。前回までのソースコードはdanielpclark/vue_exampleでご覧いただけます。

システムテストを実行するには、プロジェクトディレクトリで以下のコマンドを実行します。

rails test:system

訳注: 初めて実行する人はyarnをインストールしたうえでyarn add rails-erb-loaderを実行しておきましょう。場合によってはyarn install --ignore-enginesの実行も必要かもしれません。

リソースの作成と更新の部分でエラーが2件表示されるはずです。「Unable to find visible field “Body” that is not disabled」というエラーメッセージが表示されます。

上述のエラーはこのフィールドでのみ表示されます。これがこのテストで最初のエラーであるためです。テストの順序を変更すると、作成や更新にあるすべてのフィールドについて同様にテストが失敗します。これはテストするフィールドをCapybaraから探索する方法に関連しているはずです。

訳注: 手元のRuby 2.5.1/Rails 5.2.0環境では、最初だけ上述のエラーが1件表示されましたが、その後エラーは表示されなくなりました。springを止めてやり直しても、やはりエラーは表示されませんでした。

Railsのフォーム系メソッドでラベルや入力フィールドを生成すると、HTMLのlabel要素にforフィールドができます。ここには、対象となるあらゆる入力のidオブジェクトの名前が含まれます。私たちが独自のVueJSコンポーネントテンプレートを用いて新しいフォームを手作りしたときに、これらのフィールドについてはテストを書いていませんでした。そのため、app/javascript/form-document.vue.erbファイル内のフォーム用フィールドを変更する必要があります。

<label>Subject</label>
<input type="text" v-model="document.subject" />

<label>State</label>
<select v-model="document.state">
  <%= options_for_select(Document.states.keys, "concept") %>
</select>

<label>Body</label>
<textarea v-model="document.body"></textarea>

上を以下のように変更します。

<label for="document_subject">Subject</label>
<input id="document_subject" type="text" v-model="document.subject" />

<label for="document_state">State</label>
<select id="document_state" v-model="document.state">
  <%= options_for_select(Document.states.keys, "concept") %>
</select>

<label for="document_body">Body</label>
<textarea id="document_body" v-model="document.body"></textarea>

CapybaraにSubjectfill_inするよう指示を出すと、Capybaraはそのラベルのコンテンツとforの値を元に対象を取得し、そのforの値で使われているidを対象とします。この方法のよい点は、それに続く入力フィールドを対象として指定しなくても、このラベルを用いて同じように対象を指定できることです。

(修正後に)rails test:systemを再実行すると、同じエラーメッセージが表示されます。これはRails 5の設定でprotect_from_forgeryがテスト環境で使われていないことに関連するはずで、そのためCSRFトークンが生成されなくなっています。私たちのVueJSコードが失敗したのは、メタ属性フィールドが利用できることを明示的に要求しているためです。

これは以下のいずれかの方法で修正できます。

  • 自分のVueJSコードを編集して、CSRFトークンが存在しなくても動作するようにする(上述のアドバイス)。
  • config/environments/test.rbファイルを以下のように変更する。
# Forgery protection
config.action_controller.allow_forgery_protection = true

フロントエンド側のフォーム送信の実装によっては、ApplicationControllerに以下のコードがないと動かない可能性もあります。私たちの場合はこれは不要になります。

protect_from_forgery prepend: true

原注: 問題発生の原因を突き止めるには、RAILS_ENV=testを指定してrails serverを実行する必要があります。これにより、Documentリソースのneweditリソースを表示したときにブラウザのコンソールでJavaScriptのエラーが発生してる様子を確認できるようになります。

rails test:systemを実行すると、今度は「Update DocumentボタンやCreate Documentボタンが見つからない」というエラーメッセージに変わります。これはRailsが送信ボタンとして生成したと命名スキーマがフォームヘルパーで使われている可能性があるため、テストを更新して現在のボタン名を反映する必要があります。

test/system/documents_test.rbを開き、click_onの対象を"Create Document""Update Document"から"Submit"に変更します。これでテストを実行すると、今度は正しいflash通知が結果の中に見当たらないというメッセージが表示されますので、このflashメッセージを追加する必要があります。以下のapp/views/layouts/application.html.erbアプリケーションテンプレート内の<body>タグの内側に以下を追加します。

<% flash.each do |name, msg| -%>
  <%= content_tag :div, msg, class: name %>
<% end -%>

続いてコントローラのupdateアクションやnewアクションを更新して、適切なflash通知が表示されるようにします。app/controllers/documents_controller.rbを以下のように変更します。

def create
  @document = Document.new(document_params)

  respond_to do |format|
    if @document.save
      flash[:notice] = 'Document was successfully created.'
      format.html { redirect_to @document, notice: 'Document was successfully created.' }
      format.json { render :show, status: :created, location: @document }
    else
      format.html { render :new }
      format.json { render json: @document.errors, status: :unprocessable_entity }
    end
  end
end

def update
  respond_to do |format|
    if @document.update(document_params)
      flash[:notice] = 'Document was successfully updated.'
      format.html { redirect_to @document, notice: 'Document was successfully updated.' }
      format.json { render :show, status: :ok, location: @document }
    else
      format.html { render :edit }
      format.json { render json: @document.errors, status: :unprocessable_entity }
    end
  end
end

これでrails test:systemを実行すると、システムテストがすべてパスするようになります。


  • 次記事: Railsのフロントエンドのノウハウ#2: JavaScript編(翻訳)

関連記事

Rails 5.1以降のシステムテストをRSpecで実行する(翻訳)

Rails 5: WebSocketのマルチセッションをMiniTestとシステムテストでテストする(翻訳)

Railsのフロントエンドのノウハウ#2: JavaScript編(翻訳)

$
0
0

概要

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

記事を2本に分割しました。日本語タイトルは内容に即したものにしています。

Railsのフロントエンドのノウハウ#2: JavaScript編(翻訳)

RailsにおけるJavaScriptについての洞察

Rails開発者の多くは、JavaScriptがうまく動作しない場合の振る舞いについてなかなか把握できないことがあります。しかもRailsの場合、JavaScriptのいくつかの振る舞いについても特定の方法で変更されます。

TurbolinksやSpringを嫌う人が多いという話を聞いたことがあるかと思います。私はたまたまどちらの技術も好きですが、これらの動作を理解しておかないと、百戦錬磨のJavaScript開発者ですら頭を抱えるような事態になるかもしれません。

これら2つのライブラリについて簡単にご説明いたします。

Turbolinksは、ページの読み込みを高速化したり、フォームや一部のCRUD操作で使えるUJS機能を素早く読み込めるようにしたりします。Turbolinksを導入するとJavaScriptの「page ready hook」が変更されます。このフックは"turbolinks:load"を行うのに使われます。Turbolinksを使っていると、標準のJavaScript「page ready hook」でいくつかの問題が生じます。

Springはアプリのコードを対象とするプリローダであり、Rubyコードとフロントエンドのアセットの両方について読み込んだものを自身のプロセスにキャッシュし、次回のリクエストで使えるようにします。JavaScriptのエラーハンドリングについての知識がないと、解決に多大な日数を要するバグを追いかけるはめになるかもしれません。Springは、最初のページ読み込みから最初に操作を行った「後で」これらを機能させるためです。したがって、サイトが読み込まれるときに一部が動かないことがあっても、リンクをクリックすると動き出します。

Springの役割を詳しく調べる前に、JavaScriptがひどいコードをどのように扱うかを最初に見てみることにしましょう。

Javascriptが何らかのひどいコードに遭遇したり、評価すべきでないものを評価したりすると、JavaScriptは現在の作業を停止して、何か問題が生じていることをブラウザのコンソールに出力します。すなわち、アプリにJavaScriptの何か新しいライブラリを導入し、そこにひどいコードがあると、それ以降のコードはライブラリであろうと自分のコードであろうと動かなくなります。これはJavaScriptの通常の振る舞いであり、ひどいコードを実行する時点でコードの事項を停止するためのものです。

しかしSpringを追加した場合、アセットがキャッシュに(Turbolinksもろとも)読み込まれてひどいJavaScriptコードが評価された後、SpringはJavaScriptコードをの実行を回避しません。つまり最初のページを読み込むと、SpringやTurbolinksがアセットやライブラリをキャッシュしてより高速読み込みに備えるのです。

最初のリンクを操作してたどるまではまったく問題ないように見えることがあります。というのも、SpringはひどいJavaScriptコードの後でよいJavaScriptコードを生成するためです。最初のページ読み込みでJavaScriptの機能が不要であれば、何か問題が起きているかどうかをコンソールで確認しない限り、何も問題が起きていないように見えます。そして(productionで)コードを公開すると、「ローカルではちゃんと動く」にもかかわらず、サイトの一部がまったく突然にダウンして期待どおりに動かなくなります。

この手の「ローカルと同じ結果をproduction環境で得られない」問題のせいで、多くの人が髪の毛をかきむしりまくっています。

ここでJavaScript開発者にひとつ耳寄りな小技をご紹介します。アプリに新しいライブラリなり新しいJavaScriptコードなりを追加する場合は、常にその末尾にconsole.log("Seems okay ¯\_(ツ)_/¯");を追加しておくというものです。こうしておけばブラウザコンソールでコードがちゃんと動いていることを確認できるようになります。コンソールログにこれが出力されていれば、自分のJavaScriptコードが読み込み中に「ひどいコード」として評価されていないことがわかります。

私はBootstrap 4ライブラリをいろんなバージョンで試しましたが、その中に「ひどい」と評価されるJavaScriptコードも含まれていたことがありました。しかも私はそれを、アプリで必要とされる他のJavaScriptライブラリより前に配置していたのです。おかげで最初のページ読み込みでJavaScript関連のコードが軒並み動かず、ページをリフレッシュしたり何らかのリンク操作を行ったりした後でどうやら動くという事態になりました。私がBootstrap 4ライブラリをTurbolinksより前に置いていたために、コードの振る舞いがそのようになってしまったのです。Springがあっても最初のページ読み込みで問題なく動くように、CoffeeScriptでささやかなハックを仕込みました。

if !Turbolinks?
  location.reload

Turbolinks.dispatch("turbolinks:load")

これはローカル環境では問題なく動作したのですが、productionでは動きませんでした。Rails開発者は、"turbolinks:load"トリガがWebサイトの最初のページ読み込みで当初呼び出されないという問題をちょくちょく踏んでいます。

Turbolinks.dispatch("turbolinks:load")は、JavaScriptのその部分のコードが評価された後できるだけ早い段階でこのWebフックを即座にトリガするための方法です。これは、最初のページ読み込みの時点でJavaScriptやCoffeeScriptのスクリプトをトリガしたい方にとって役に立ちます。

ページ読み込み時にCoffeeScriptをトリガする場合、ちょっと一手間かける必要があります。というのも、CoffeeScriptコードはすべて1つの関数内にラップされていて、スコープが保護されているためです。Turbolinks.dispatch("turoblinks:load")メソッドを使うことで、このあたりのややこしさが少しマシになります。

Springを使っている場合に有用なハックはif !Turbolinks?; location.reloadしかありません。これが必要になるのは、JavaScriptの何らかの「ひどいコード」が評価されるdevelopment環境でJavaScriptコードを読み込む場合だけです。これを使うのではなく、先ほどのconsole.logテクニックを用いてJavaScriptコードの実行に成功したかどうかを確認しましょう。その後で、「ひどいコード」として評価される状況を改善しましょう。

最後に

Capybaraは、フロントエンドのエクスペリエンス全体のテストをシンプルに書ける素晴らしいライブラリです。RailsはCapybaraテストの大半を代わりに生成してくれるので、それを読んで学べば同じようなテストを今後も書き続けられます。面倒な設定はRailsが肩代わりしてくれるので、フロントエンドのテストを始めるのがとても簡単になります。

Rails 5のもうひとつうれしい点は、システムテストで例外が発生すると、ブラウザウィンドウの内容のスクリーンショットをtmp/screenshots/に自動保存してくれるようになったことです。画面とスクリーンショットを比較してテストの問題を特定しやすくなりました。

Railsのライブラリと併用した場合のJavaScriptの振る舞いを理解しておけば、地雷を避けてデバッグ時間を大幅に節約できるようになります。JavaScriptの振る舞いを正しく理解すれば、これまでうんざりさせられていた強力なツールたちをさらに使いこなせるようになります。TurbolinksSpringは、効用をと利用法を理解しておけば素晴らしいツールになるのです。

おかげで近頃はRails開発者でよかったと思えます。フロントエンドテストで大いに楽しみましょう!

Twitter: x“Rails Frontend Testing with JavaScript Insights” via @6ftdan

関連記事

Rails 5.1以降のシステムテストをRSpecで実行する(翻訳)

Rails 5: WebSocketのマルチセッションをMiniTestとシステムテストでテストする(翻訳)

週刊Railsウォッチ(20180615)TTY gemとHTTPClient gemは優秀、Rubyの謎フリップフロップ、ちょいゆるRubyスタイルガイドほか

$
0
0

こんにちは、hachi8833です。仙台疲れが今になって来たような気がしないでもありません。雨の降らない国に行きたいです。

晴耕雨読のウォッチ、いってみましょう。

各記事冒頭には⚓でパーマリンクを置いてあります: 社内やTwitterでの議論などにどうぞ

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

今回も主に公式のコミット情報からです。

⚓TableDefinition#columnでカラム定義が重複すると例外を出すように修正

# activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb#L354
       def column(name, type, options = {})
         name = name.to_s
         type = type.to_sym if type
         options = options.dup

-        if @columns_hash[name] && @columns_hash[name].primary_key?
-          raise ArgumentError, "you can't redefine the primary key column '#{name}'. To define a custom primary key, pass { id: false } to create_table."
+        if @columns_hash[name]
+          if @columns_hash[name].primary_key?
+            raise ArgumentError, "you can't redefine the primary key column '#{name}'. To define a custom primary key, pass { id: false } to create_table."
+          else
+            raise ArgumentError, "you can't define an already defined column '#{name}'."
+          end
         end

         index_options = options.delete(:index)
         index(name, index_options.is_a?(Hash) ? index_options : {}) if index_options
         @columns_hash[name] = new_column_definition(name, type, options)
         self
       end

つっつきボイス:TableDefinitionなんてのがあるのね」「マイグレーションがらみのようです」「create_tablet.columnが呼ばれるときの重複チェックを今回修正したと: テストコードがわかりやすい↓」「普通やらかさないエラーだろうけどraiseはしとかないとね😋

# activerecord/test/cases/migration/change_schema_test.rb#L199
+      def test_create_table_raises_when_defining_existing_column
+        error = assert_raise(ArgumentError) do
+          connection.create_table :testings do |t|
+            t.column :testing_column, :string
+            t.column :testing_column, :integer
+          end
+        end
+
+        assert_equal "you can't define an already defined column 'testing_column'.", error.message
+      end

参考: Ruby on Rails 5.2 / ActiveRecord::ConnectionAdapters::TableDefinition — DevDocs

columnがconnection_adapters/abstract/schema_definitions.rbにあるということは、abstractすなわち複数のコネクタで共通で使われるメソッドなんでしょうね: ぱっと見ですが」「abstractクラスなのでそれで合ってると思います🧐: 細かくはちゃんと追わないといけないですが、上のエラーももしかするとDBMSの種類によっては通っちゃうことがあったのかもしれないですね: SQLite3とか割と作りが雑なんでカラム名かぶっても通っちゃうとかありそう、知らんけど😆」「😆

「そういえば先週のウォッチでもSQLite3ラブラブ❤な意見が出てました」「ちなみにSQLite3、かなり速いっすよ🕶: Win32アプリみたいなPCローカルアプリで使うDBMSとしては読み込み速度とかめっちゃ優秀」「あー、そういえば以前いた職場の社内ツールでも中でSQLite3が走ってました」「ただSQLite3って型とかが割と雑で、確か内部では数値なんかも文字列で保存してたような覚えがあるんですよ」「『保存してSQLっぽく検索できればそれでいいのっ』って感じなんでしょうね😓」「だからこそ速いんでしょうけどね🤓

参考: SQLite Home Page


sqlite.orgより

⚓ActiveSupport::Time.zone.atTime::atの引数の不揃いを解消

# 同PRより
# Before:
Time.at(946684800, 123456.789).nsec       #=> 123456789
Time.zone.at(946684800, 123456.789).nsec  #=> ArgumentError (wrong number of arguments (given 2, expected 1))
# After:
Time.at(946684800, 123456.789).nsec       #=> 123456789
Time.zone.at(946684800, 123456.789).nsec  #=> 123456789
# activesupport/lib/active_support/values/time_zone.rb#L357
-    def at(secs)
-      Time.at(secs).utc.in_time_zone(self)
+    #
+    # A second argument can be supplied to specify sub-second precision.
+    #
+    #   Time.zone = 'Hawaii'                # => "Hawaii"
+    #   Time.at(946684800, 123456.789).nsec # => 123456789
+    def at(*args)
+      Time.at(*args).utc.in_time_zone(self)
     end

つっつきボイス:Time.atTime.zone.atを揃えたようです」「おや?これで見るとTime.zone.atの方を変えてる?: Time.zone.atの方が使用頻度多いし(つかRailsではそもそもこっちを使うべき👮‍♂️)、Time.atをじかに使うことってほとんどないと思うんだけどなー」「それもそうですね…🤔」「breaking changeになりそうに見える🕶」「そのときはRailsアップグレードガイドに載るんでしょうね」

参考: Ruby on Rails 5.2 / Time::at — DevDocs
参考: Ruby on Rails 5.2 / Time::zone — DevDocs

⚓.dup.freeze-ショートハンドに置き換え

# activemodel/lib/active_model/attributes.rb#L103
       def self.set_name_cache(name, value)
         const_name = "ATTR_#{name}"
         unless const_defined? const_name
-          const_set const_name, value.dup.freeze
+          const_set const_name, -value
         end
       end

つっつきボイス: 「出たな-🤣」「あ、これ以前のウォッチでもみんなで『キモチワルイー』って言ってた文字列dup&freezeのショートハンドですね😆」「確かRuby 2.5の機能じゃなかったっけ?」

毎度のことですが、記号は検索しづらいです😭。マイナス記号-です。

参考: instance method String#-@ (Ruby 2.5.0)

selfがfreeze されている文字列の場合、selfを返します。 freezeされていない場合は元の文字列のfreezeされた (できる限り既存の) 複製を返します。
ruby-lang.orgより

参考: NEWS for Ruby 2.5.0 (Ruby 2.5.0)
参考: Feature #13077: [PATCH] introduce String#fstring method - Ruby trunk - Ruby Issue Tracking System

⚓記号の雑学

ついでながら、マイナス記号とハイフン記号とダッシュ記号は本来は別の記号だったのですが、記号を増やしようがないタイプライターの時代に1つのキーで代用されてしまい(長いemダッシュは--としたり)、それがそのままASCIIなどにも持ち込まれてハイフンマイナスという苦し紛れな名前が付けられ、Unicodeで別記号が用意された今も地味に混乱を呼び続けてます。ハイフン記号とマイナス記号とダッシュ記号(emダッシュとenダッシュ)の使い分けには闇が横たわっています。ヨーロッパの一部には、ハイフンとダッシュを取り違えて使うとマジギレする国がありますのでご用心。見た目1ドットぐらいしか違わないんですけどね…😫



Dash vs. Hyphen ~ IngliszTiczer.plより

参考: ハイフンマイナス - Wikipedia
参考: ハイフン - Wikipedia
参考: ダッシュ (記号) - Wikipedia

⚓重複した子レコードを親がsaveしないよう修正

# 同issueより再現コード
# モデル

class Parent < ApplicationRecord
  has_many :children
end

class Child < ApplicationRecord
  belongs_to :parent
  validates :name, uniqueness: true
end

# コード
parent = Parent.new(children: [Child.new(name: 'Wiske'), Child.new(name: 'Wiske')])
parent.save
parent.errors.any?  # エラーになるはずが、ならない

つっつきボイス:validates uniqueness: trueだから本来エラーにならないといけないやつ: へー、この書き方↓するとすり抜けちゃってたのか😇」「😇」「これは下手すると既存のアプリでも知らないうちにエラーを作り込んじゃってたりすることがあるかも?自分はこの書き方はしないけどねっ🕶

Parent.new(children: [Child.new(name: 'Wiske'), Child.new(name: 'Wiske')])

「ついでに聞いちゃいますけど、今見ているコードにあるreflection↓っていう言葉は、ここではどういう意味が込められているんでしょうか?(英語的にいっぱい意味がありすぎるので🤯)」「reflectionというと、メソッドオブジェクトを取ってきてそれをゴニョゴニョしたいなんてときに使いますね」「あー、メタプログラミング的な?」「たぶんプログラミング関連ではそれ以外の意味でreflectionという言葉が出てくることはあまりないと思うので💦

# activerecord/lib/active_record/autosave_association.rb#L381
       def save_collection_association(reflection)
         if association = association_instance_get(reflection.name)
           autosave = reflection.options[:autosave]
...
               if autosave != false && (@new_record_before_save || record.new_record?)
                 if autosave
                   saved = association.insert_record(record, false)
                 elsif !reflection.nested?
-                  association_saved = association.insert_record(record)
                   if reflection.validate?
-                    saved = association_saved
+                    valid = association_valid?(reflection, record, index)
+                    saved = valid ? association.insert_record(record, false) : false
+                  else
+                    association.insert_record(record)
                   end
                 end
               elsif autosave

「たとえばですが、reflectionという名前の変数があるとすると、その中には状況によってさまざまなオブジェクトが動的に入る可能性があるよ、という意図が強調される感じですかね」「おー、『あのクラスとあのクラスだけが入ると思うなよ』っていうイメージですか」

「あくまでイメージというか概念寄りの話ですが、reflectionという言葉が使われていると、そこにどんなオブジェクトが入るかわからないし、どんなオブジェクトが来てもいいという感じ: C言語の(void *)キャストとか、Javaの(object)キャストなんかが概念的に似ているかな」「なるほどー!😀

参考: C言語: 汎用ポインタ
参考: 強く型付けされているJavaの理解に必修の“型変換” (2/3):【改訂版】Eclipseではじめるプログラミング(18) - @IT

「このコードならreflection.validate?とあるから、普通ならreflectionにはActiveRecordオブジェクトやActiveModelのインスタンスが入るんだろうけど、それ以外のオブジェクトが入る可能性だってあるんだぜ、という主張を込めて自分なら使うかな😎、ここでは知らんけどw」「😃😃

reflectionには「反射」「反省(self reflection)」「反映」などなどいろんな意味がありますが(「反」の文字が共通してますね🧐)、ポイントは意味が受動的なところかなと思いました。反映もapplyで表すと人間が能動的にやるイメージですが、reflectだと受動的というか性質に従って自動的に行われる、というニュアンスが強いと思います。reflectionがメタプロの文脈でよく使われるのもそれなのかなと。

人間の態度や状況が何かにreflectするとは、そうした態度や状況(またはそう思われるもの)がそこに存在することが示されていることを表す。
Cobuldより大意

⚓初期化ブロックがbuild_recordにあるのをbefore_addに移動

以下はコミットログから見繕いました。

# https://github.com/rails/rails/commit/c256d02f010228d260b408e8b7fda0a4bcb33f39#diff-20f545c453ee24942b6f7ae565e9e369R104
       def build(attributes = {}, &block)
         if attributes.is_a?(Array)
           attributes.collect { |attr| build(attr, &block) }
         else
-          add_to_target(build_record(attributes)) do |record|
-            yield(record) if block_given?
-          end
+          add_to_target(build_record(attributes, &block))
         end
       end

つっつきボイス: 「改修範囲が広いので取りあえずこれだけ引用してみました」「ここだけ見て言うと、add_to_targetというのはおそらくフックを追加するメソッドかな: で、そういうコールバック登録の枠組みが既にあるならそれに揃えようよってことなんだと思う」「😀

# rails/activerecord/lib/active_record/associations/collection_association.rb#L282
      def add_to_target(record, skip_callbacks = false, &block)
        if association_scope.distinct_value
          index = @target.index(record)
        end
        replace_on_target(record, index, skip_callbacks, &block)
      end

⚓force_equality?をpublic APIに

43ef00eの続きだそうです。

# https://github.com/rails/rails/pull/33067/files#diff-e66146ba2ab968e251944a764e4ed967R54
+      def force_equality?(value)
+        coder.respond_to?(:object_class) && value.is_a?(coder.object_class)
+      end

つっつきボイス:@kamipoさんが何度かに分けて修正したようです」「force_equality?ってメソッドがあるのか: どうやらrespond_to?(:object_class)でオブジェクトのクラスまで含めて等しいかどうかをチェックするやつみたいですね」「おー」「たとえばシリアライズしたときのパラメータのリストは同じでも、オブジェクトの種類が違うものがやってくることがあったりするけど、そういうのも検出するということでしょうね」「😃

「このメソッドなら、たとえばSTI(Single Table Inheritance)なんかでそれが親クラスのオブジェクトなのか子クラスのオブジェクトなのか、なんてのも見分けられるはず」「なるほど!」「オブジェクトが参照しているデータベースが同じだとシリアライズした後では区別できなくなっちゃうけど、シリアライズより前にそういうところをチェックするときに使えるんじゃないかな?ってね🤓」「😋」「これが欲しい気持ち、よくわかる」

Rails: STI(Single Table Inheritance)でハマったところ

参考: Rails ActiveRecordのSTI(Single Table Inheritance)の使い方 | EasyRamble

⚓Rails

⚓Bundler 2リリース間近のチェックリスト

Bundler関連ということで関連してこちらも。


つっつきボイス: 「リリースはまだみたいなので、こういうことをやりますリストですね」「そういえば今はもうRubyをバージョンアップすると最新のBundlerが入るようになったんだっけ?どっちだったっけ?」

そういえば2.5でのBundler標準化は延期されたのでした↓。

「チェックリストをざざーっと見た感じでは互換性の心配ほとんどなさそうというか、こっちでやることあまりないかも?: つか互換性なかったらRubyバージョンを単純に上げたときに即死だし😇」「Bundlerといえば、HerokuのBundlerが最新でないのでwarning出たりたまにつっかえたりするんですよね(今はどうだったかな💦)」「まーHerokuの場合Herokuの環境に身を任せないといけないので、面倒を見てくれる範囲は大きいけど自分はコンテナでやりたいかなー😂」「😀」「Linuxサーバーの面倒なところ触りたくないっという人にはもちろんHerokuはおすすめですね: Railsチュートリアルは今もHeroku使ってるんだったかな?」「今は違ってたような…?」

後で調べたら、Railsチュートリアルは今もHeroku使ってました

⚓graphql-ruby: Railsとも連携


graphql-ruby.orgより

今回のウォッチは何となくShopify成分が多めでした。


つっつきボイス: 「これ扱ったことなかったっけ?」「なかったっぽかったので(と思ったらありました💦)」「このロゴは素晴らしいですね😍」「グラフ理論のグラフをうまくあしらってるし😎

参考: apollo + rails(graphqlサーバー)でファイルをアップロードするmutationを作る方法 - Qiita

⚓Active Record 5.2のメモリ肥大化を探る

このSam Saffaronさんのブログは良記事多いですね。翻訳許可をいただいたので今後出したいと思います。

# 同記事より
a = []
Topic.limit(1000).each do |u|
   a << u.id
end

つっつきボイス: 「あー、↑こうやって書けばそりゃ確かに肥大化するナ」「eachを使ってるから?」「eachだけじゃなくてu.idを参照してるから: このu.idを評価しないといけないのでu.idが呼ばれるたびにTopicというモデルのオブジェクトが生成される」「そっちかー」「しかもそれをa <<で代入しちゃってるからこのループの間このモデルオブジェクトがまったく解放されない」「😲」「pluckすると速いって記事に書いてあるのもたぶんそれで、pluckならモデルオブジェクトを保持する必要ないはずだし」「pluckは配列を返しさえすればいいですしね」

「確かこの間のRails Developers Meetup 2018でもまさにこの辺の話をしていた人がいたと思う」「自分見てなかったかも😓

後でmorimorihogeさんからセッション名を教えていただきました↓。

参考: railsdm2018で「ActiveRecordデータ処理アンチパターン」を発表しました - Hack Your Design!

「まーでもActiveRecordの挙動とかlazy loadingのあたりなんかを忖度できるようにならないと、上のような書き方がヤバイ理由は見た瞬間にはわからないでしょうねー🧐」「そこが経験が必要なところかー😃」「あかんのはu.idという参照の仕方です😎: 終わってからGCすれば消えますけど(たぶんね)」「覚えます!」

参考: Ruby on Rails 5.2 / Enumerable#pluck — DevDocs

最近知ったRailsの便利なメソッド

⚓Rails 5で「関連付け先のないレコード」を使う(Ruby Weeklyより)


つっつきボイス: 「記事のスタイリングとかシンタックスハイライトがちと読みづらい…」「関連付けのない場合ってことなのかな?ざざっと見た感じ、assosiation先のレコードがなくても取得したい場合にLEFT JOINでの外部結合をActiveRecordでどう書くかって話のように見える」「時間ないので次行きましょうー」

  • Rails 4までは生SQLでJOIN
# 同記事より
User.join('left outer join posts on posts.user_id = users.id')
  .where(posts: {user_id: nil})
  .first
  • Rails 5はちょっとだけクリーンで読みやすい気がする
# 同記事より
User.left_joins(:posts)
  .where(posts: {user_id: nil})
  .first

⚓Rails 5.2のcredentialはセキュアではない?(RubyFlowより)


つっつきボイス: 「GitHub Wikiにざざっと書いた、1ページで収まる内容ですね」「これはわかりみあるヤツ: Rails 5.2のcrenentialは、言ってみれば例のTwelve-Factor Apps↓の原則3『設定を環境変数に格納する』に反してるんですよ」「あー」

参考: The Twelve-Factor App (日本語訳)


12factor.netより

「原理主義者としては許せないでしょうけど、そこまで行かなくても自分もこの辺は微妙: マスターキーの扱いっていろいろ難しいんですよ」「というと?」「チーム開発をしていると、開発メンバーが入れ替わるたびにマスターキーを変えないといけなくなるから: そして現実にメンバーの出入りはよくあることだし」「あー!🤫」「しかもマスターキーの変更履歴がリポジトリに残るのも、どうもねー🤔」「記事の人はnot secureとか書いてるけど、単にcredentialが好きじゃないんでしょうねー😆自分も使うつもりたぶんないし」「😆

⚓deep_pluck gemがRails 5.2に対応


つっつきボイス:pluckの入れ子版みたいなこのdeep_pluckウォッチで扱ったことありましたね: たぶんバッチとか書くときなんかに優秀なヤツ👍」「😀」「これもActiveRecordオブジェクトを生成しないから速いんだとしたら、自分なら生SQL書いちゃう、かなー?😆」「😆

⚓その他Rails

週刊Railsウォッチ(20170407)N+1問題解決のトレードオフ、Capybaraのテスト効率を上げる5つのコツほか


そろそろJSの組み合わせが爆発しそう。


つっつきボイス: 「ちょうど今日のチームミーティングでもWebpackについて発表ありましたけど、やっぱりWebpackerを知るにはWebpackそのものを知っておかないと結局後で困ることになりますね😆」「😆


webpack.js.orgより



つっつきボイス: 「テストって最初のうちはどの程度までみっちり書けばいいのか真剣に悩んじゃいました」「それはもう誰かに決めてもらうのが早い😎」「🤣」「ただ思うのは、よほどシビアなアプリでもない限り、カバレッジの数字だけを当てにしてもあまり意味がないかなってことですね」「カバレッジの値が増えたからといって本当に必要な部分がカバーできてるかどうかはまた別なのか🙂



つっつきボイス: 「2位のMagentoって知りませんでした」「自分も😁」「それにしてもShopify、いつの間にかこんないい位置についてるなー: ユーザー数はともかく、捌いているトラフィック数は凄い👁」「Shopify、Railsですしね🤠」「EC-CUBEと比較してみたいところだけど、日本製でほぼ日本市場だけだろうから比較は難しいかなー」



つっつきボイス:mizchiさんいいこと言うなー: フロントエンドの強い人で今は確かフリーランスだったかな?」「さっきのreflectionの話もそうでしたけど、コードを書いた人の意図を読み取る能力と、読み取れるようにコードを書く能力ってやっぱり重要ですね🧔🏽

そのうちIPA試験あたりにコード読解問題とか出たりするんでしょうか。


⚓Ruby trunkより

⚓Unicode 11のグルジア語の大文字小文字の取り扱いをどうしよう?


http://www.unicode.org/versions/Unicode11.0.0/ch07.pdf (Section 7.7, Georgian, pp. 320-321) より

Geogianが「グルジア語の」とも「ジョージアの」とも読めてしまうので、いちいち断りが必要です。グルジアというとスターリンの出身国というイメージ。なおグルジア語とロシア語には共通部分がほぼありません。


https://ja.wikipedia.org/wiki/%E3%82%B0%E3%83%AB%E3%82%B8%E3%82%A2%E8%AA%9E#/media/File:Kartvelian_languages.svg より

参考: グルジア語 - Wikipedia


つっつきボイス: 「これはもう多言語マニアがいないとどうしようもない😇」「issueでもそう言ってました😆

⚓GRND_NONBLOCKを設定せずにgetrandomが複数呼ばれるとロックする

if which ruby >/dev/null && which gem >/dev/null; then
    PATH="$(ruby -e 'puts Gem.user_dir')/bin:$PATH"
fi

つっつきボイス:GRND_NONBLOCKって、またLinuxに新しいフラグが出たのか」「(Linuxでしたか😓)」「よく見つけたなこれ感」

参考: getrandom(2) - Linux manual page

⚓Procの行番号/カラム番号、できればfirstやlastも取りたい

# rspec-parameterized gemのサンプル

  describe "lambda parameter" do
    where(:a, :b, :answer) do
      [
        [1 , 2 , -> {should == 3}],
        [5 , 8 , -> {should == 13}],
        [0 , 0 , -> {should == 0}]
      ]
    end

    with_them do
      subject {a + b}
      it "should do additions" do
        self.instance_exec(&answer)
      end
    end
  end

# 出力例

  lambda parameter
    a: 1, b: 2, answer: -> {should == 3}
      should do additions
    a: 5, b: 8, answer: -> {should == 13}
      should do additions
    a: 0, b: 0, answer: -> {should == 0}
      should do additions

tagomorisさんとjoker1007さんからのリクエストです。


つっつきボイス: 「最初気づかなかったけど、joker1007さんがそういう感じのgem作ってたそうです↓」「RSpecでwhere(:a, :b, :answer)みたいにwhereで指定したいってことのようだ: Procが複数になるとつらいとか、これは確かにやるなら言語でサポートする方がいいでしょうね😋

⚓Ruby

⚓Relaxed Ruby Style: ちょいゆるRubyスタイルガイド

Version 2.3とあるのはRubyのことなのかこのスタイルガイドのことなのか、どちらなんでしょう?


つっつきボイス: 「rubocop.ymlに軽くパッチを当てて使うようです」「パッチじゃなくて、元々rubocopにはinherit_fromというデフォルト設定を読み込む機能があるので、それを使って読み込んだ上で自分達のルールとの差分を上書きする、みたいな使い方ですね」「あー、そうでした😓」「うん、こういうのいいよね: BPS社内でも標準Rubocop.yml定めたいと思ってる🤓

⚓ramda-ruby:

# 同リポジトリより: トランスデューサー
  appender = R.flip(R.append)

  xform = R.map(R.add(10))
  R.transduce(xform, appender, [], [1, 2, 3, 4]) # [11, 12, 13, 14]

  xform = R.filter(:odd?.to_proc)
  R.transduce(xform, appender, [], [1, 2, 3, 4]) # [1, 3]

  xform = R.compose(R.map(R.add(10)), R.take(2))
  R.transduce(xform, appender, [], [1, 2, 3, 4]) # [11, 12]

  xform = R.compose(R.filter(:odd?.to_proc), R.take(2))
  R.transduce(xform, R.add, 100, [1, 2, 3, 4, 5]) # 104)
  R.transduce(xform, appender, [], [1, 2, 3, 4, 5]) # [1, 3])
  R.into([], xform, [1, 2, 3, 4, 5]) # [1, 3])

JavaScriptのRamdajsをRubyに移植したものだそうです。名前からlambdaのもじりという感じで、ramdajs.comには「育ちすぎたram(子羊)じゃないんだなー、たぶん」とありました。ramだ。


つっつきボイス: 「ramda: いいのかその読み方で😆」「たぶん子羊じゃないといいつつ、シンボルマークは羊ですね😄」「関数型の追求というよりは、フィルタ的な便利ツール集という印象」


ramdajs.comより

参考: ラムダ計算 - Wikipedia

トランスデューサーそのものはめちゃめちゃ意味が広いですが、ここでは関数型言語よりの何かなんでしょうね。何も見ずに「translator + reducer」かなと推測。

参考: トランスデューサー - Wikipedia

⚓Rubyとmallocの問題

⚓TTY: Ruby錬金術師の秘薬

RubyKaigi 2018で自分が見てなかったやつです(´・ω・`)。


つっつきボイス: 「お、これ自分見ましたよ: いい内容👍」「😃」「TTYってマルチスレッド対応のプログレスバーとかいろいろ機能揃ってますよ」

「このTTYってrails new的にファイルをどさっと作るんで、腰を据えて本格的なCLIアプリを作るためのテンプレート的なものでしょうね: 気楽にさっと書くにはかなり重装備な感じ」「この間のウォッチで取り上げたoptparseとはまた違うんでしょうか?」「optparseは引数処理を楽に書けるヤツなのでまた違いますね🕶
「TTYは、railsコマンドとかsystemdコマンドみたいなものすごく複雑なCLIを見通しよく作れますね: その気になればaptyarnだって作れちゃう😆」「🤣」「TTY上でmarkdownすら書ける」「何だかすげー」

「ただまあ、今こんなごついCLI書くぐらいならWebアプリにするよなという気もすると言えばする」「確かにー」「でも最初CLIで完成させてからそれをWebアプリ化するという流れはありかなとも思うし: CLIならバッチとかとっても扱いやすいし、融通が利きやすいし」「たぶんデバッグもしやすいし😃

「『こういうCLIはRubyで作ればいいじゃん』っていう文化になったらいいなーと思うし」「こうやってTTYなんかで作ったRubyのCLIを、mrubyとかでコンパイルして配布できればいいのにとも思うし: 結局RubyのCLIって配布がだいたい問題になるんですよね」「確かにー」「CLIでのGo言語の強みはrun anywhereですからね🕶

⚓go-mruby: mrubyのGoバインディング

// 同リポジトリより
package main

import (
    "fmt"
    "github.com/mitchellh/go-mruby"
)

func main() {
    mrb := mruby.NewMrb()
    defer mrb.Close()

    // Our custom function we'll expose to Ruby. The first return
    // value is what to return from the func and the second is an
    // exception to raise (if any).
    addFunc := func(m *mruby.Mrb, self *mruby.MrbValue) (mruby.Value, mruby.Value) {
        args := m.GetArgs()
        return mruby.Int(args[0].Fixnum() + args[1].Fixnum()), nil
    }

    // Lets define a custom class and a class method we can call.
    class := mrb.DefineClass("Example", nil)
    class.DefineClassMethod("add", addFunc, mruby.ArgsReq(2))

    // Let's call it and inspect the result
    result, err := mrb.LoadString(`Example.add(12, 30)`)
    if err != nil {
        panic(err.Error())
    }

    // This will output "Result: 42"
    fmt.Printf("Result: %s\n", result.String())
}

ついでにquartzという、RubyからGoプログラムを呼び出すgemも見つけたのですが、RubygemにGoのnative extensionを含める感じではなさそう…なぜかこの方面はさっぱり発展しません(´・ω・`)。

⚓httprbはいいぞ(Ruby Weeklyより)


同リポジトリより

# 同リポジトリより
HTTP.post("http://example.com/upload", form: { file: HTTP::FormData::File.new(io) })

つっつきボイス: 「このhttprb/httpってどうでしょう?」「少なくとも表の青いところにある標準のNet::HTTPはまず使うことはないと思う、というかつらすぎて使いたくない😆」「😆」「😆


同記事より

参考: class Net::HTTP (Ruby 2.5.0)

「この表の中で一番メジャーなHTTPクライアントって、赤いところにあるHTTPClient↓なんじゃないかなー: 少なくとも自分的にはベスト」

「ただ、HTTPClientはREADMEがそっけないんですが、実はそこから地味にリンク貼られているRDocのAPIドキュメントがすごくしっかり書かれてるんですよ↓」「ほー!!」「ここ読むとわかりますけど、プロキシもちゃんと対応してるし、BASIC認証の先にあるBASIC認証の対応とか、環境変数からプロキシ渡すなんてのもできます」「いいこと聞いた!😍」「😍

「表の緑色のところにあるFaraday↓もひと頃流行ったけど、Faradayの抽象化は人によって好みが分かれるかもしれない: クローラーを書くとかならFaradayが向いてますね」「逆にステータスコードが取りたいとかヘッダを確認したいとかならHTTPClientがいい」

「で、本題のhttprbは表の分類からもHTTPClientと同じところを狙ってるというのが取りあえずは見て取れますね」「😃」「おっ?httprbのインストールはgem install httpだって: この名前よく取れたなー😲」「Rubygems.orgで決めてるんでしたっけ?」「早いもの勝ちです😎

⚓Rubyで書かれたワールドカップ試合情報CLI(RubyFlowより)

# 同リポジトリより
$ footty              # Defaults to today's world cup 2018 matches

今日は以下でした。

#1 Thu Jun/14       Russia (RUS) vs Saudi Arabia (KSA) Group A  @ Luzhniki Stadium, Moscow

つっつきボイス: 「試合結果というより、『今日ある試合は何だったっけ?』用っぽいです」「こういう文化っていいですよねー」

⚓Rubyの謎機能: フリップフロップ

# 同記事より
irb(main):021:0> (1..10).each {|i| puts i if i==3..i==5 }
3
4
5
=> 1..10

つっつきボイス:Less Feature-Rich, More Funという記事を翻訳していて、一番最後にRubyのフリップフロップという謎の機能について言及されていたので」「何だこれ…確かに謎🤔

「あー、if i==3..i==5..の前と後の条件がフリップフロップになってるのか!😲」「😲」「😲」「こんなのがコードレビューに出てきたらつらすぎ😭」「Ruby Gold試験になら出そうですね…😓

⚓その他Ruby




RubyKaigiの応募要項です。


ほのっとしちゃいました。その後英語版↓も出ました。

⚓クラウド/コンテナ/Linux

以下時間切れのため、つっつきはここまでです🙇。後追いで何か追記するかもしれません。

⚓Linuxのloadavgの問題を追求

はてブで見つけました。


つっつきボイス: 「これはとてもいい記事」

⚓Googleのgvisorすごいかも

この間のウォッチで軽く取り上げたgvisorですが、TCFM #22の前半がこの話題でもちきりでした。

⚓ワンライナー特集

⚓GitLabが立て続けに機能を拡大

最新の記事ではありませんが一応。


参考: GitLabがGoogleのKubernetes Engineを統合、コンテナアプリケーションのデプロイが超簡単に | TechCrunch Japan

⚓その他クラウド/コンテナ/Linux

⚓SQL

⚓PostgreSQLのメモリ設定(Postgres Weeklyより)

⚓ロシア発: PostgreSQL 10の論理レプリケーションの復旧(Postgres Weeklyより)


同記事より

かなり長いです。

⚓はじめてのマテリアライズド・ビュー(Postgres Weeklyより)


同記事より

参考: マテリアライズドビュー - Wikipedia

⚓JavaScript

⚓Vue Native: VuejsでネイティブWebアプリを作るフレームワーク


vue-native.ioより

⚓PhantomJSの開発が正式に終了し、アーカイブ化(JSer.infoより)

昨年のウォッチでもお伝えしたPhantomJSが正式に終了しました。お疲れさまです。

週刊Railsウォッチ(20171026)factory_girlが突然factory_botに改名、Ruby Prize最終候補者決定、PhantomJS廃止、FireFoxのFireBug終了ほか

参考: PhantomJSの開発が終了しリポジトリがアーカイブ化された - JSer.info

⚓mobx: JSのステート管理ライブラリ(JSer.infoより)


mobx.js.orgより

JavaScriptのステート管理はVuejsにもあったりReduxもあったりと賑やかですね。js.orgに集結している?


redux.js.orgより

JavaScript: Reduxが必要なとき/不要なとき(翻訳)

⚓ExcelでJavaScriptがサポート

TypeScriptを直接サポートするつもりはないそうです。

参考: Microsoft、Excelカスタム関数としてJavaScriptのサポートを発表

⚓その他JavaScript

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

⚓直感に反する「なぜか覚えられないUI」(Frontend Weeklyより)

↑著名な認知心理学者のDonald NormanことDon Normanの名前から「Norman door」と呼ばれているそうです。てっきりこのドアの発明者かと思ってしまいました。
引くかと思ったら押す、押すかと思ったら引くような、「押す/引く」表示がないとガンガンに間違えるドアだそうです。

参考: Urban Dictionary: Norman Door
参考: ドナルド・ノーマン - Wikipedia

⚓テスティングを楽しく学びたい人向けのサイトなど

  • 元記事: テストラジオ — テスティングの話題が豊富なポッドキャスト

既に60回を超えているんですね。凄い。


testerchan.hatenadiary.comより

⚓Apollo Clientとは


apollographql.comより

⚓言語よろずの間

⚓Mage: Goのタスクランナー


同リポジトリより

英語の古語にある「メイジ(魔法使い)」だよなと思いつつ、日本人なのでつい「マゲ」かと思ってしまいました。

⚓Fo: Goで関数型やってみる言語


play.folang.orgより

なお、Goでジェネリクスを欲しいと思ったことが今のところありませんでした。

参考: ジェネリックプログラミング - Wikipedia

⚓Stack Overflowのデベロッパーアンケート結果2018年版

毎度お騒がせ。

参考: 2018年 人気&嫌われプログラミング言語トップ25- Stack Overflow | マイナビニュース

⚓その他言語

参考: 依存型 - Wikipedia





⚓その他

⚓Gitコマンドを異世界転生モノで解説するよ

⚓らめぇそれ

⚓「不正指令電磁的記録に関する罪」とは

はてブで知りました。

⚓Windowsに新しいアプリインストール形式「MSIX」が登場予定

⚓その他のその他





社内勉強会のお題が「トランザクションとロック」だったので、そういうときについ思い出す動画です。

⚓番外

⚓今どきの法科学

⚓あれそんなにヤバイ物質だったのか

⚓学校でがっつり教えられた人の立場は

⚓香害

⚓リアルポケモン認定したい

⚓火星で有機物発見か

地球に巨大隕石がぶつかったか何かで火星に飛来した小型隕石によるコンタミの可能性が気になります。南極は一面真っ白で月の石や火星の石がちょくちょく見つかるので、逆もありそうな気がしてしまいます。

参考: 南極サイエンス基地 > 南極隕石


今週は以上です。

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

週刊Railsウォッチ(20180608)特集「RubyKaigi 2018後の祭り」、`Enumerable#index_with`は優秀、コントローラから`@`を消し去るほか

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

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

Rails公式ニュース

Ruby Weekly

RubyFlow

160928_1638_XvIP4h

Postgres Weekly

postgres_weekly_banner

Frontend Weekly

frontendweekly_banner_captured

週刊Railsウォッチ(20180622)Railsの需要未だ巨大、Unicode 11.0リリース、WebDriverがW3Cで勧告、Flutter.io、2封筒問題ほか

$
0
0

こんにちは、hachi8833です。私が気まぐれに応援するサッカーチームは必ず負けるので、どこも応援しないようがんばります。

各記事冒頭には⚓でパーマリンクを置いてあります: 社内やTwitterでの議論などにどうぞ
まだ検討段階ですが、週刊Railsウォッチの刊行日を月曜日に移動するかもしれません

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

特記ない限りRails 6.0向けです。

既存association削除時の探索をハッシュに変えて高速化

# activerecord/lib/active_record/associations/collection_association.rb#L391
         def remove_records(existing_records, records, method)
           records.each { |record| callback(:before_remove, record) }

           delete_records(existing_records, method) if existing_records.any?
-          records.each { |record| target.delete(record) }
+          hashed_records = records.group_by { |record| record }
+          target.select! { |record| !hashed_records[record] }

           records.each { |record| callback(:after_remove, record) }
         end

GDPR対応でユーザーや「いいね👍」をごっそり削除することになった人、いるよね? dependent: destroyオプションのアルゴリズムが更新されて(計算量が)二次から一次に削減されました。これ大好き😋
公式ニュースより大意


つっつきボイス:eachで全回しでdeleteしてたのを、group_byしてからhashed_recordsで削除したと」「quadratic(二次)からlinear(一次)って、ここでは計算量のことでいいんですよね?」「ですね🧐O(n^2)からO(n)に削減されたやつ」「お、例のsgrifさんが『self.target -= recordsだけでいけるんじゃ?』ってコメントしてる🤔」「おー、確かにこの書き方は演算子がオーバーライドされてればできるな: 面白い😋」

参考: ランダウの記号 - Wikipedia
参考: 一次方程式 - Wikipedia — linear equation
参考: 二次方程式 - Wikipedia — quadratic equation

storeアクセサで従来のprefixの他にsuffixも使えるようになった

# 同PRより
store :settings, accessors: [ :two_factor_auth ], coder: JSON, _prefix: true
# => accessor will be model.settings_two_factor_auth
store_accessor :settings, :secret_question, _prefix: 'config'
# => accessor will be model.config_secret_question
store :settings, accessors: [ :login_retry ], _suffix: 'setting'
# => accessor will be model.login_retry_setting

つっつきボイス: 「おー、storeにsuffixもねー: 既存のデータベースでsuffixを使うやつがあるんだろうし、prefixが使えるならsuffixも欲しいというのはまあワカル」「指定したsuffixが自動で追加されるんですね」「自分はあんまり使わないかなー?😆」

Active Modelのデフォルトのエラーメッセージ表示方法を改良

#{attribute} #{message}#{message}, full_messageに変更し、以下のどれでも使えるようにしつつ高速化も図ったようです。

en:
  errors:
    format: '%{message}'
en:
  activemodel:
    errors:
      models:
        person:
          format: '%{message}'
en:
  activemodel:
    errors:
      models:
        person:
          attributes:
            name:
              format: '%{message}'

つっつきボイス:full_messageがオーバーライドできるようになって、しかもi18nなのか!そういえば今まではデフォルトのエラーメッセージが生書きされてたような気がする」「この改修はどの辺がうれしいんでしょうか?」「今まではデフォルトのエラーメッセージが完全に英語の語順になっちゃってたんで、日本語環境だとeachで回してゴニョゴニョしないといけないとか、いろいろ使いにくかったんですよ」「あー!なるほど😲ローカライズでもメッセージ内のプレースホルダーの位置なんかを言語によって変えないといけないけど、まさにその問題か」「そのあたりをi18nでもう少し何とかできるようになったということのようだ」「上はyamlファイルなんですね?」「ですです: i18nのfull_messageのテンプレートをyamlでオーバーライドできるようになったということでしょうね: 嬉しい人には嬉しいのかも?」「私は嬉しいです😄」

developmentモードのeager loadingを修正

Rails 5.1/5.2でconfig.eager_load = trueするとサーバーがロックすることがあったそうです。

# actionpack/lib/action_dispatch/journey/routes.rb#L51
       def ast
         @ast ||= begin
           asts = anchored_routes.map(&:ast)
-          Nodes::Or.new(asts) unless asts.empty?
+          Nodes::Or.new(asts)
         end
       end

つっつきボイス:config.eager_load = trueでロックとか恐ろしいw💀」「修正はめちゃシンプル: astだから抽象構文木ですね」「どうやらこいつが何かのはずみで同時に呼ばれるとヤバかったんだろうなー: コミットメッセージを見ると、Railsエンジンが複数ある場合にRouteSetsが空になる問題だったのか!」「😲」「自分はあまり踏まなそうなバグかな?」

参考: Rails エンジン入門 | Rails ガイド
参考: 抽象構文木 - Wikipedia

テスト中のパラメータエンコーディングにto_queryではなくRackを使用

# actionpack/lib/action_controller/test_case.rb#L104
           case content_mime_type.to_sym
           when nil
             raise "Unknown Content-Type: #{content_type}"
           when :json
             data = ActiveSupport::JSON.encode(non_path_parameters)
           when :xml
             data = non_path_parameters.to_xml
           when :url_encoded_form
-            data = non_path_parameters.to_query
+            data = Rack::Utils.build_nested_query(non_path_parameters)
           else
             @custom_param_parsers[content_mime_type.symbol] = ->(_) { non_path_parameters }
             data = non_path_parameters.to_query
           end

お馴染みAaron Pattersonさんによる修正です。


つっつきボイス: 「ちょうどMatz vs Aaron Patterson対談の記事があったので貼ってみました↑💎」「GitHub Satellite!😃HTTPのクエリパラメータのあたりで、to_queryだとエンコーディング前にソートされてたのをRackのに切り替えて修正したということかな」

associationのコレクション更新に-=を使うよう修正

これは自分で見繕いました。

この修正によってメモリ上で変更されたassociationもloadedとしてマーキングされるので、次回のアクセスでデータベースクエリの発生を回避できる。
同commitより大意

# activerecord/lib/active_record/associations/collection_association.rb#L399
         def remove_records(existing_records, records, method)
           records.each { |record| callback(:before_remove, record) }

           delete_records(existing_records, method) if existing_records.any?
-          hashed_records = records.group_by { |record| record }
-          target.select! { |record| !hashed_records[record] }
+          self.target -= records

           records.each { |record| callback(:after_remove, record) }
         end

つっつきボイス: 「あ、これちょうど今回の一番上の#29939の続きじゃないですか❤️」「ほんとだ: sgrifさんのサジェスチョンどおりですね🙂」「どこまで最適化されてるかはわかりませんが😆」「😆」

Rails

Railsへの需要は未だ巨大

サクッと読める記事です。


つっつきボイス: 「実際に使われている既存のRailsアプリがとても増えたし、エンジニアの需要も普通に多いというか減ってないという実感」「LinkedInの求職の件数(サンフランシスコのベイエリアなど)をチェックするというのはうまいですね: 言語ではRubyは必ずしも上位ではないけど、フレームワークでの求職数は圧勝しているというあたり」「ところでMatzがRailsネタでツイートするのは割と珍しいかも?」「確かにー」

tokaido: RailsアプリをmacOSアプリにする

Railsガイドに載ってました。東海道?


つっつきボイス: 「単独のMacアプリにできるということみたいです: こういうのがあったって知りませんでした」「自分も😆」「よく見ると最後の更新が3年前…Railsをまるっと飲み込んだら結構なサイズになりそう?」

AMPって今どうよ(Frontend Weeklyより)


つっつきボイス: 「AMPはもう普通に使われてると思うし: あと最近AMP JSも使えるようになるみたいだし」「あ、そうだった😲」

参考: AMP とは – AMP


ampproject.orgより

GraphQLは未来なのか?(Frontend Weeklyより)

「GraphQL is not your data model」という見出しがとりあえず気になりました。


つっつきボイス: 「日本だとGraphQLに夢を見ている人が多い印象🌠」「😆」「日本はフロントエンドとサーバーサイドを別の人がやってることが多いという事情もあるし: もしかするとソシャゲ界隈でGraphQL使いたい人が多いかもね🕶」

RailsでGraphQL APIをつくる: Part 1 – GraphQLとは何か(翻訳)

アセットパイプラインディレクトリの脆弱性


つっつきボイス: 「Herokuの記事でした」「productionでconfig.assets.compile = trueしなければ大丈夫だそうです(オレオレアプリもセーフ🤑)」「アセットのコンパイルって、ごくごく稀にオンにしてみることがあったりしたナ: コンパイルがうまく動かなかったときとか😆」「やったことなかった…」「Directory Traversalはなかなかエグい☠️けど、アプリがコンテナに置かれててchrootしているとかならそこまで深刻ではないのかもしれないけど、ね」

参考: ディレクトリトラバーサル - Wikipedia

git push -fはヤバい

参考: git push -f をやめて --force-with-lease を使おう - Qiita


つっつきボイス: 「実はこの間Gobyちゃんのリポジトリでgit push -fやらかしちゃって😓」「マジでw🤣」「今後やらかさないようgit config --system receive.denyNonFastForwards trueを唱えました」「master権限持っててぶっ壊したらどうしようもないけど、開発者ならprotectedブランチ的なものを立てるとか、masterブランチとdevelopブランチに直接pushできないようプロテクトしておくべきではあったかな😎」「そうでした😵」

「最近よく聞くのは、『featureブランチでは基本的にpush -fしない』という運用ですね: 重複更新が入ってもとりあえずコミットでつなげて、最後マージするときにsquashする」「なるほど!」「もちろんプロジェクト次第ですけどね: 個人的には、そのfeatureブランチを自分しか触ってないならpush -fしてもいいんじゃね?とは思うけど」

「いつもは自分のブランチでpush -fしてたんですが、今回はうっかりst0012さんのPRブランチにpushしちゃって💦」「人のブランチにgit push -fは、そりゃ戦争🔫💣ものだな🤣」「さすがにムッてたと思います…ほんとすみません🙏」「最初にfeatureブランチができたらそれ以外の人はpushできないようになったりするといいかもしれないですけどね😎、まあgit push -fは使わざるを得ないときもあるんでドンマイ」「😃」

Goby: Rubyライクな言語(2)Goby言語の全貌を一発で理解できる解説スライドを公開しました!

Railsアプリのデータベースロジックを失わない方法(RubyFlowより)

# 同記事より
app/
  sql/
    application.sql
    user.sql
    product.sql

つっつきボイス: 「sql/ディレクトリ掘ってるのが気になって」「それは普通に生SQLを置くところでしょうね: ははぁこれはマイグレーションについての記事か」「というと?」「通常のdb:migrateに限らない、データベースの更新とかも含むマイグレーションで使う生SQLはこういうところに置こうよという趣旨」「😃」

「ついでですが、これみたいに1回しか実行しない生SQLをどこに置くかっていつも悩ましいんですよ」「確かにー」「1回こっきりならスニペットで書くか、あるいはActiveRecord使うならrakeタスクにするとか: SQL一発だけならこの記事みたいな管理方法もありかなとは思う」

5つの手順で完璧なenumを作る(RubyFlowより)


つっつきボイス: 「完璧なenum🤣: まずはarrayじゃなくてhashでやろうとある、自分もhashでやるのが好きだし😋」

# 同記事より
class Catalog < ActiveRecord::Base
  enum localization: [:home, :foreign, :none]
end
0 -> home
1 -> foreign
2 -> none

「そうそう、業務システムなんかで、enumをデータベースに0とか1の値で入れることにこだわるケースがあったりするんですが、それだとソース見ないと値の意味がわからないんじゃね?って思うことしばしば😆」「それ確かにー」「自分はstringで保存したい派: PostgreSQLだと確か文字列enumが使えて、MySQLにもenum型が確かあって、そういうのを使えばできるし」

# 同記事より
class Catalog < ActiveRecord::Base
  enum localization: { home: 0, foreign: 1, none: 2 }
end

「お、この記事はPostgreSQL enum使ってるじゃん↓: エライ!」「これだとデータベース内部は数値でもクエリでは文字列が使えるんですね」「そうそう、やるなら自分はぜひこっちにしたい😋」「😃」

# 同記事より
class AddStatusToCatalogs < ActiveRecord::Migration[5.1]
  def up
    execute <<-SQL
      CREATE TYPE catalog_status AS ENUM ('published', 'unpublished', 'not_set');
    SQL
    add_column :catalogs, :status, :catalogs_status
  end

  def down
    remove_column :catalogs, :status
    execute <<-SQL
      DROP TYPE catalog_status;
    SQL
  end
end

「で究極のソリューションは、と: おー、これはたぶんPostgreSQLの機能だと思うんですが、型を追加してますね、CREATE TYPE catalog_status AS ENUMで」「😃」「createのenum定義に直接型を書く代わりに、catalog_statusという名前を付けてそれを使ってマイグレーションをすると: これは確かにキレイに書ける」「おー、ぽすぐれ側でやろうということですね」「まあ今度はこのcatalog_statusって定義はどこなんだ?と思われるかもしれないし、ここまでぽすぐれに頼っていいんだろうかとも思いますが😆」

# 同記事より
# マイグレーション
class AddStatusToCatalogs < ActiveRecord::Migration[5.1]
  def up
    execute <<-SQL
      CREATE TYPE catalog_status AS ENUM ('published', 'unpublished', 'not_set');
    SQL
    add_column :catalogs, :status, :catalogs_status
    add_index :catalogs, :status
  end

  def down
    remove_column :catalogs, :status
    execute <<-SQL
      DROP TYPE catalog_status;
    SQL
  end
end

# ValueObject:
class CatalogStatus
  STATUSES = %w(published unpublished not_set).freeze

  def initialize(status)
    @status = status
  end

  # what you need here
end

RailsアプリでReactをホットリロードに使う(RubyFlowより)


同記事より


つっつきボイス: 「ホットリロードは最近のWebアプリではよく見かけますね: フロントだと欲しいヤツ」「Webpackも使ってるし」

Railsのcredential

先週Railsのcredentialの話が出てたので。


つっつきボイス: 「この間の話は要するにcredentialをリポジトリにコミットする場合にマスターキーをチームでどううまく扱うかという問題でしたが、この記事みたいにマスターキーをAWSのKMSに保存すれば、KMS内の生の鍵には開発者のアクセスを許す必要がないし、KMSに置かれた鍵をIAM経由で使うようにすれば、プロジェクトから抜けたメンバーをIAM Groupから外すだけでマスターキーにアクセスできなくなるということですね」「😃」

Rails APIとJWT認証とVuejsでSPAする(RubyFlowより)


つっつきボイス: 「JWTはWeb界隈では新し目の認証方式ですね」



jwt.ioより

最近よかったRuby/Rails記事: 2018年前半(RubyFlowより)


つっつきボイス: 「ざっと見たところこれまでウォッチで既に取り上げたり翻訳したりした記事も載ってて、ちょっとだけ『勝った』感ありました😆」「😆」

RabbitMQはSidekiqと同等以上だと思う: 前編(翻訳)

search_flip: ElasticsearchでクエリをチェインするDSL(RubyFlowより)

Finally, SearchFlip supports ElasticSearch 1.x, 2.x, 5.x, 6.x. Check section Feature Support for version dependent features.
同リポジトリより


つっつきボイス: 「よくあるやつなのかなと思って」「お、これElasticsearchの6.xに対応してるのがエライですね!: たしかこの間のRails Developers Meetupだったかな、Elasticsearch向けの何かのRubyのライブラリがElasticsearchの5.xまでしか対応してなくて新しい機能はまだ使えないねみたいな話があったと思ったんだけど、6.xに対応しているという理由でこのgemを使う人がいるかも?」

ActiveSupport::MessageEncryptor


つっつきボイス: 「そうそう、Rubyでencrypter/decrypter使うとだいたいこんな感じになりますね: しかしActiveSupportの割にはローレベルな書き方😆」

# 同記事より
ENCRYPT_CIPHER = 'aes-256-cbc'
ENCRYPTOR = begin
  key_len = ActiveSupport::MessageEncryptor
            .key_len(ENCRYPT_CIPHER)
  key = ActiveSupport::KeyGenerator
        .new("<環境から挿入したkey>")
        .generate_key("<環境から挿入したsalt>", key_len)
  ActiveSupport::MessageEncryptor.new(
    key,
    cipher:     ENCRYPT_CIPHER,
    digest:     'SHA1',
    serializer: Marshal,
  )
end

「その後に『デフォルト値には頼らない』って書いてますけど、これは使う処理系やライブラリによってデフォルト値が違うことがあるんですよ」「というと?」「以前一度はまったのが、PHPのライブラリでencryptしたものをRubyのライブラリでdecryptしようとしたら、どっちかのライブラリでゼロフィル(ゼロ値で埋めること)やられてて復元できなかったことあったんですよ😭: たしか相当無理やりに突破した」「ありゃ~」「ライブラリを素直に無加工で使ってくれればいいものを、そうやってデフォルト値変えられたりするとつらい」

「そういえば私も正規表現ライブラリを異なる言語間で共通で使おうとして似たようなハマり方したことありました😢」「それもあるある: eregなのかpregなのかとかね、といっても最近pregしか見かけないけど😎」

参考: preg_match関数と正規表現の理解を再度見直す - Qiita

その他Rails



trello.comより

Ruby trunkより

提案: 第2のGCヒープ「Transient heap」

ko1さんによる提案です。

mallocが管理するヒープに代わる第2のGCヒープ「Transient heap」のMRIへの導入を提案します。利用されるGCアルゴリズムは「世代的」「コピー」GCアルゴリズムに近いものです。これによってmallocされたヒープの問題を軽減できると見込みます。
同issueより大意


つっつきボイス: 「mallocの手前に別のメモリ管理レイヤを置くという感じ: 結局OSのメモリ管理とRubyのメモリ管理という2層があるから、mallocだといろいろうまくいかないところがあるんでしょうね」「ふーむ🤔」「今日の社内勉強会でキャッシュのしくみの話をしたけど、キャッシュの場合と似た感じで、同じ仕事をする層が複数あるとそれぞれがてんでに局所最適に動作して全体としてうまくいかない、なんてことが起きがちですね」

Ruby: mallocでマルチスレッドプログラムのメモリが倍増する理由(翻訳)

Time.strptime%jを使うユリウス暦で正しく動かない

# 同issueより
require "time"
require "date"

### Works for Dates ###
parsed_date = Date.strptime("15300", "%y%j")
expected_date = Date.new(2015, 10, 27)
# Does not raise
raise "dates not equal" if expected_date != parsed_date


### Does not work for Time ###
parsed_time = Time.strptime("15300", "%y%j")
expected_time = Time.new(2015, 10, 27)
# Raises
raise "times not equal" if expected_time != parsed_time

つっつきボイス:Dateだと動くのにTimeだと動かないというバグ」「Julian date=ユリウス暦の日付」

参考: ユリウス暦 - Wikipedia

ローマ教皇グレゴリウス13世が1582年、ユリウス暦に換えて、太陽年との誤差を修正したグレゴリオ暦を制定・実施したが、今でもグレゴリオ暦を採用せずユリウス暦を使用している教会・地域が存在する。
Wikipediaより

Timeにタイムゾーンを設定する公式のAPIが欲しい

# 同issueより
>> ENV['TZ'] = 'America/New_York'
>> Time.now.zone
=> "EDT"
>> ENV['TZ'] = 'Europe/London'
>> Time.now.zone
=> "BST"

ActiveSupport::TimeWithZoneでやってるようなのを定めたいそうです。

参考: ActiveSupport::TimeWithZone


つっつきボイス: 「タイムゾーンは誰がというかどこで一元管理するのかという問題がそもそもあるんですけど、最終的にはOSの環境変数が頼りですね: この機能はActiveSupportから持ってきてもいいと思う」

Ruby

StripeのRuby lint


sorbet.runより


つっつきボイス: 「Stripeはこの辺に力入れてますね: RubyKaigiのこの発表見られなかった😢」「この間のウォッチでもちょっとだけ扱いましたが一応」「sig(foo: Integer).returns(String)みたいにシグネチャを指定するのか: Matz好みでないやり方😆」「構文変わっちゃいますもんね…」

# sorget.runより
class A
  sig(foo: Integer).returns(String)
  def bar(foo)
    foo.to_s
  end
end

def main
  A.new.barr(91)
  A.new.bar("91")
end

グループ名がない場合にスキップする修正

Windows対応のようです。

mruby-meta-circularとは


つっつきボイス: 「RiteVMってmrubyのVMなんですね: Riteというとつい春の祭典を連想してしまいます🙂」

参考: mruby Virtual Machine(RiteVM) — mrubook 1.0 documentation

「今回のRubyKaigi 2018を見てて思ったんですが、mrubyって組み込み系という印象がそれまで強かったけど、最近はむしろH2Oみたいなミドルウェア系での活躍が目立っててそちらで見直されてる感」「確かにH2Oの成功がアピールした感じありますね😃」

参考: H2O - the optimized HTTP/2 server


h2o.examp1e.netより

別件ですがこんなのも。

dining-table: Rubyでテーブルをきれいに作るgem

# 同リポジトリより
class CarTableWithConfigBlocks < DiningTable::Table
  def define
    table_id = options[:table_id]  # custom option, see 'Options' above

    presenter.table_config do |config|
      config.table.class = 'table-class'
      config.table.id    = table_id || 'table-id'
      config.thead.class = 'thead-class'
    end if presenter.type?(:html)

    presenter.row_config do |config, index, object|
      if index == :header
        config.tr.class = 'header-tr'
        config.th.class = 'header-th'
      elsif index == :footer
        config.tr.class = 'footer-tr'
      else  # normal row
        config.tr.class = index.odd? ? 'odd' : 'even'
        config.tr.class += ' lowstock' if object.stock < 10
      end
    end if presenter.type?(:html)

    column :brand
    column :stock, footer: 'Footer text'
  end
end

つっつきボイス: 「名前が😆」「一度こういう感じに作っておけばHTMLテーブルの他にも後でcsvとかxlsxとかいろんな形式で出せるのか: カスタマイズしたテーブルを扱うときなんかにちょっといいかも😋」

MRuby-Zest: mrubyのオーディオGUIフレームワーク

長ったらしいif-else条件のリファクタリング(RubyFlowより)

# 同記事より
# Before
# Metrics/MethodLength: Method has too many lines. [13/10]
def foo1(number)
  if number == 1
    'one'
  elsif number == 2
    'two'
  elsif number == 3
    'three'
  elsif number == 4
    'four'
  elsif number == 5
    'five'
  else
    'many'
  end
end

# After
DICTIONARY = {
  1 => 'one',
  2 => 'two',
  3 => 'three',
  4 => 'four',
  5 => 'five'
}.freeze

def foo2(number)
  DICTIONARY[number] || 'many'
end

つっつきボイス: 「うん、これは定番のリファクタリングですね: よく使うというか最初からこう書くし」「そうでしたか!😲」「DICTIONARYを作ることで意味が明確になるし、条件を後で変えたり追加したりするのも楽だし、何より読みやすい❤️」

「↓こんなふうにlambda使うのはちょっとスゴイな😉、でもこれも自分使ってるわ」「😀」「こうやってlambda使ったり出力をevalしたりするとかすると最強感💪」

# 同記事より
# After
module SearchService2
  PARAMS_MAP = {
    'id' => ->(value) { { id: value } },
    'first_name' => ->(value) { { 'first_name' => value } },
    'last_name' => ->(value) { { 'last_name' => value } },
    'email' => ->(value) { { 'email' => value } },
    'city' => ->(value) { { 'city' => value } },
    'gender' => ->(value) { { gender: value } },
    'height_after' => ->(value) { { height: { '$gt' => value } } },
    'height_before' => ->(value) { { height: { '$lt' => value } } },
    'weight_after' => ->(value) { { weight: { '$gt' => value } } },
    'weight_before' => ->(value) { { weight: { '$lt' => value } } },
  }.freeze
...

その他Ruby



つっつきボイス: 「言われてみればそうだったわー😲: あんまり使わないけど…」






TCFM出演?


RubyKaigi 2018での生ペアプロ動画も上がってました。

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

Linux高速化の話題2つ


つっつきボイス: 「最近のLinuxネットワークの詳細は正直よくわからん😆: まともに追いかけてたのはカーネル2.6ぐらいまでだったし」「あー、じゃLinuxも結構がっつり更新されてるんですね」「ちゃんと更新されてますよ😎」

書籍「実践 パケット解析 第3版」

OSI階層とは

このネタ割と前からあったんですね。

参考: OSI参照モデル: 比喩 - Wikipedia

Google I/O 2018全動画

その他クラウド/コンテナ/インフラ/Linux





つっつきボイス: 「これ定期的に出回るネタ😎」「🤣」




SQL

試さないと損するPostgreSQLの機能(Postgres Weeklyより)

AWS RedshitとPostgreSQLはどこが違うか(Postgres Weeklyより)

pgdeltastream: PostgreSQLの更新をリアルタイムに観測(Postgres Weeklyより)


同リポジトリより

PostgreSQLの関数

はてブで見かけました。

JavaScript

AirbnbがReact Nativeをやめた話

はてブなど各所で話題ですね。ついAirBnBと書いてしまいそうです。

↑先越されちゃいました(´・ω・`)。


つっつきボイス: 「これもmizchiさんが早速アンサー記事↓書いてくれてますね」

参考: いつ ReactNative を使っても大丈夫か - mizchi’s blog

flutter.ioとAndroid fuchsia OS

社内Slackで密かに見守られています。Dart言語ベースだそうです。


より

参考: Dart - Wikipedia

Flutterが何らかの形でFuchsia OSに採用されるかもしれないとも。

参考: iOSとAndroid、両方のアプリを一度に作れちゃう優れモノ! Googleの隠れた戦略が見える「Flutter SDK」 | ギズモード・ジャパン

Parcel 1.9.0リリース(JSer.infoより)

その他JavaScript


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

「WebDriver」がW3CでRECに

参考: 「WebDriver」がW3Cの勧告に到達。Webブラウザのテスト自動化などを実現 - Publickey

GDPRどころじゃないのかも

Creative Commonsも反対を表明しています。つかTechRachoも他人事ではありません。

参考: ハイパーリンクを貼るだけで著作権料がかかる通称「リンク税」がEUで導入されようとしている - GIGAZINE

CSS+JSのアニメーションロゴ

Unicode 11.0リリース


blog.unicode.orgより


つっつきボイス: 「とりあえず絵文字増えましたね❤️」「海老🍤」

参考: Emoji Recently Added, v11.0


unicode.orgより

リグレッションとデグレーションの違い

その他CSS/HTML/フロントエンド/テスト


言語よろずの間

マイクロソフトのBlazorとは

GoからPHPに帰ってきた話

話は少し逸れますが、先日のRubyKaigi 2018のドリンクアップでPHP開発者の方とお話しする機会があり、PHPでは英語情報を探さないといけない状況がほぼまったくないと伺いました。

書籍「Go言語でつくるインタプリタ」の続編「Go言語でつくるコンパイラ」がリリース

↓参考まで: こちらは例のGobyちゃんに最も強く影響を与えたリファレンスです。

SHA1コリジョン

各所でバズってますね。

モンティ・ホール問題より手ごわい「2封筒問題」

BPS社内でも盛り上がりました。

↑この問題について最も引用されているという短い論文です。読んでもいませんが、三浦俊彦『可能世界の哲学』によると、「交換すると双方が得になるのは事前期待値が無限大の場合に限られる」ことがこの論文で示されているんだそうです。

同書によると、この2封筒問題を扱うサイトや書籍は必ず炎上するらしく、出版界では密かに疎まれているんだそうです。Railsウォッチもついに炎上?

参考: 数学 - 二封筒問題
参考: 可能世界論 - Wikipedia

同書で紹介されていた以下の本もちょっと気になります。命名の助けになるか混乱の元になるかわかりませんが。

参考: 藤川直也『名前に何の意味があるのか 固有名の哲学』 - logical cypher scape

Rust 1.27リリース

// 同記事より
// SIMDなし
let lots_of_3s = (&[-123.456f32; 128][..]).iter()
    .map(|v| {
        9.0 * v.abs().sqrt().sqrt().recip().ceil().sqrt() - 4.0 - 2.0
    })
    .collect::<Vec<f32>>();

// SIMDあり
let lots_of_3s = (&[-123.456f32; 128][..]).simd_iter()
    .simd_map(f32s(0.0), |v| {
        f32s(9.0) * v.abs().sqrt().rsqrt().ceil().sqrt() - f32s(4.0) - f32s(2.0)
    })
    .scalar_collect();

参考: SIMD - Wikipedia

その他言語


たぶん反語?
私は.vimrcは最小限しか設定してません。



あらゆるGoリポジトリにgofmt -w .をかけてまわるようです。



その他

3D XPointメモリとは

macOSの「クイックルック」は残る

SQLite使ってるって初めて知りました。

参考: macOSの「クイックルック」は暗号化ドライブのファイルまでキャッシュしており、データは永続的に保管されいつでも閲覧できる - GIGAZINE

人感センサー

これ改造がとっても楽なんだそうです。

その他のその他


純粋に欲しいです。けどCD-ROM販売…









つっつきボイス: 「あーこれ何だっけ??」「タイガー式計算機ですね: 自分も実物見たことありませんが😭」

朝永振一郎のエッセイでこれと朝晩格闘してたという記述を見たぐらいでした。

参考: 機械式計算機 - Wikipedia



↑後はワイヤレスになってくれれば完璧ですね。


番外

氷河期の写実的な壁画は自閉症スペクトラムの表れという説

参考: 氷河期の壁画が驚くほどに写実的だったのは「描き手が自閉症だったから」という説明 - GIGAZINE

明和電機健在

参考: 明和電機 - Wikipedia

特大ステッカー

変わり筐体

極太マジックハンド

過酷な環境で酸素を生むバクテリア

参考: 人類の火星移住で酸素の供給源として活躍するかもしれないバクテリアが発見される - GIGAZINE

WiFiで壁の向こうを透視

既にWiFiでの室内の物体の位置特定ってできてた気も。

材料は星

極大から極小までをインタラクティブに


htwins.netより

元になった以下の「Powers of Ten」はかなり昔にIBMが制作した定番の教育コンテンツだそうです。


つっつきボイス: 「やっぱり最後は宇宙ネタ?」「そう決めてるわけではないんですが、何となくで😆」


今週は以上です。

おたより発掘

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

週刊Railsウォッチ(20180615)TTY gemとHTTPClient gemは優秀、Rubyの謎フリップフロップ、ちょいゆるRubyスタイルガイドほか

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

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

Rails公式ニュース

RubyFlow

160928_1638_XvIP4h

Postgres Weekly

postgres_weekly_banner

Frontend Weekly

frontendweekly_banner_captured

JavaScript Weekly

javascriptweekly_logo_captured

JSer.info

jser.info_logo_captured

Rails: jQueryをVue.jsに置き換える方法(翻訳)

$
0
0

概要

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

Rails: jQueryをVue.jsに置き換える方法(翻訳)

Rails開発者を何年もやっている方であれば、おそらくjQueryをフロントエンド開発のデフォルトオプションとして用いていることでしょう。だいぶ前のバージョンのRailsではRailsコア開発者がjQueryを標準として提供し始め、ほどなく標準になりました。当時のjQeuryはJavaScriptライブラリの筆頭であり、非常に便利でした。

その後月日は流れましたが、jQueryは現在もRailsのデフォルトオプションであり続けています。しかし現在であれば、クライアント側コードがさらに複雑になったためにもう少し別のライブラリが必要とされていることでしょう。React、Angular.js、Vue.jsなどをお試しになったことだと思いますが、最小限の努力で既存のアプリや新規アプリに簡単に統合するのであれば、この中のどれか1つだけを使うことになります。

私もjQueryを長年使ってきて今や習慣となりました。新しくRailsアプリを作ればそこにjQueryが入っているのが既に当たり前なので、jQueryをすぐ使えるようになっています。

何年も前に、私はAngular 1を好きになっていることに気付きました。始めるときの敷居がとても低かったからです。しかし当時もアプリの初期化やコントローラへの書き込み、依存性の注入(DI)で余分な設定が必要でした。

Angular 1は、MVC(MVVM)アプローチで設計されているアプリではうまくいきます。

しかし既存のアプリでjQueryのDOM操作コードが大量に含まれていて、とっ散らかったコードをもっとサポートしやすいものに置き換えたいとしましょう。たとえば以下のようなコードを置き換えたいとします(もちろん最適化可能なのは見え見えですが、あくまで例ということで)。

$(document).ready(function(){

   ...
   $('#some-radio-button1').on('click', function(){
     if (`$(this).is(':checked')) {
       // 「アクティブな」クラスを削除し
       // 関連するブロックを表示するブロックをいくつか隠蔽

     } else {
       // 上と逆の処理
     }
   });
});

Vue.jsに置き換える理由

なぜ私がjQueryをVue.jsに置き換えることをおすすめするかというと、Vue.jsは単に複雑なJavaScriptを書くときに便利というだけではなく、DOM操作のようなシンプルなタスクを行いたい場合にも使えるからです。これで要件がすべて満たされるのであれば、Vueに乗り換えるのはよい考えでしょう。そして今後ルーティングやステート管理などの複雑なタスクを解決する必要が生じた場合にもVueを使えます。

そこで、jQueryコードが大量に使われている既存のプロジェクトがあり、散らかりまくったイベントハンドラを消し去りたいのであれば、ぜひともVue.jsを試してみるべきです。

手始めに置き換えてみる

歴戦のRails開発者で、現在もSprocketsでアセットを管理しているのであれば、Vue.jsを単にダウンロードしてvendor/assets/javascriptsフォルダに保存します。

次に、メインのJavaScriptマニフェストファイル(application.js)でVueをrequireします。

//= require jquery
//= require jquery_ujs
//= require bootstrap
//= require vue

次にVueのインスタンスを生成して、HTMLコード内の何らかの要素にアタッチする必要があります。そのためには、別途以下のvue_app.js(またはvue_app.coffee)ファイルをapp/assets/javascriptsフォルダの下に置きます。

window.vueApp = new Vue
  el: '.off-canvas-container'
  data:
    ...

これでもうVue.jsを使えるようになりました。

Vue.jsをさらに使いこなす

この時点で、Vueインスタンスのdataセクションに自分のデータを追加し、何らかのハンドラをmethodsセクションに書くことができます。しかし、Vue.jsのコアユニットである「コンポーネント」を使う方がよいでしょう。

Vue.jsを使い続ける最も簡単な方法は、既存のRailsビューを用い、HTMLのいくつかの部分をコンポーネントでラップすることです。やり方を見てみることにしましょう。

たとえば、以下のapp/views/sellers/print_labels/new.html.erbに関連する何らかのjQueryコードがあるとします。

これは出荷先住所(shipping address)フォームで、入力はデフォルトで無効になっています。ユーザーが「鉛筆」アイコンをクリックするとフォームのフィールドが入力可能になり、[Save]ボタンが表示されます。[Save]をクリックすると、フォームは元の状態に戻ります。

このjQueryコードをシンプルなVue.jsコンポーネントに置き換えるために、app/assets/javascripts/components/print_labels.coffeeファイルに以下のようなコードを作成します(このcomponentsフォルダをapplication.jsでrequireするのを忘れずに)。

Vue.component 'print-labels',
  data: ->
    isEditingAddress: false

続いてこれをRailsのビューで使います。

ここでいくつか注意点があります。

  • このコンポーネントテンプレートをRailsのビューやパーシャルの中に置いておきたい場合は、inline-templateオプションを使うべきです。
  • 初期化とレンダリングの後にこのコンポーネントを表示するには、v-cloakオプションが必要です。
  • ここではonclickイベントハンドラのアタッチに@clickを用いています(複雑なコードはコンポーネントのmethodsに切り出せます)。

以上で、短いVue.jsコンポーネント(変数はたった1つ!)を用いてjQueryコードを消し去れるようになりました。

今後はこのやり方に沿って好きに進めましょう。皆さまがVue.jsで幸せになれますように。

本記事を気に入っていただけましたら、ぜひ元記事の「✋」をクリックしてください。

関連記事

Rails 5: Webpacker公式README — Webpack v4対応版(翻訳)

【保存版】Rails 5 Webpacker公式ドキュメントの歩き方+追加情報

週刊Railsウォッチ(20180813)Rails 5.2.1リリース、sanitize_sql_arrayは5.2からpublicだった、Dev.toがRailsアプリのソースを公開ほか

$
0
0

こんにちは、hachi8833です。自宅のエアコンの室内機からジャージャー水漏れしたのでこいつ↓でドレーンパイプを吸ったらコガネムシの破片が転がり出てきました🐞

  • 各記事冒頭には⚓でパーマリンクを置いてあります: 社内やTwitterでの議論などにどうぞ
  • 「つっつきボイス」はRailsウォッチ公開前ドラフトを社内有志でつっついたときの会話です👄

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

Rails 5.2.1が8/7に正式にリリースされました🎉🎉

早速記念写真。

その後のコミットを見ると、Railsガイドの修正が目立ちます。

なお、References to changes in Rails 5.2という変更点まとめ記事をHacklinesで見つけました。網羅しているかどうかはわかりませんが、トリビアな変更を避けているっぽいです。


つっつきボイス: 「お、これで5.2系もマイナーバージョンが1つ上がって安定度が増すかな😋」「😆書籍も初版は誤植が付き物ですしね」「実際gem install railsでインストールされるようになってから本格的にバグが見つかるし😎

⚓ActiveRecordのsanitize_sql_*系メソッドが5.2で既にpublicメソッドに

先週のではなく2017年のy-yagiさんによる改修でした。

# activerecord/test/cases/sanitize_test.rb#L12
  def test_sanitize_sql_array_handles_string_interpolation
    quoted_bambi = ActiveRecord::Base.connection.quote_string("Bambi")
-    assert_equal "name='#{quoted_bambi}'", Binary.send(:sanitize_sql_array, ["name='%s'", "Bambi"])
-    assert_equal "name='#{quoted_bambi}'", Binary.send(:sanitize_sql_array, ["name='%s'", "Bambi".mb_chars])
+    assert_equal "name='#{quoted_bambi}'", Binary.sanitize_sql_array(["name='%s'", "Bambi"])
+    assert_equal "name='#{quoted_bambi}'", Binary.sanitize_sql_array(["name='%s'", "Bambi".mb_chars])
    quoted_bambi_and_thumper = ActiveRecord::Base.connection.quote_string("Bambi\nand\nThumper")
-    assert_equal "name='#{quoted_bambi_and_thumper}'", Binary.send(:sanitize_sql_array, ["name='%s'", "Bambi\nand\nThumper"])
-    assert_equal "name='#{quoted_bambi_and_thumper}'", Binary.send(:sanitize_sql_array, ["name='%s'", "Bambi\nand\nThumper".mb_chars])
+    assert_equal "name='#{quoted_bambi_and_thumper}'", Binary.sanitize_sql_array(["name='%s'", "Bambi\nand\nThumper"])
+    assert_equal "name='#{quoted_bambi_and_thumper}'", Binary.sanitize_sql_array(["name='%s'", "Bambi\nand\nThumper".mb_chars])
  end

もうsendで叩かなくて済むということですね。


つっつきボイス: 「おおこれは前から何かと話しに上がってたヤツ: 自分もsendで叩いてたし」「やっぱり使ってたんですね?」「sanitize_sql_arrayとかは生SQLを書く人には絶対に必要です😤」「これはSQLインジェクション防止のために超大事なヤツっす」「なぜ今までこれがprivateだったのかホント不思議なくらいで、ツイートにもあるけど誰もが同じこと思ってた」「sendすれば使えますけどねっ🕶書き方としてよくないというだけで」

「生SQLで変数のプレースホルダーが複数出てきたりすると結局sanitize_sql_arrayとかでやる以外に方法はないですね: 当時自力で追いかけましたもん🤓」「2017年に入ってたのにウォッチで見逃してたとは…😅これだけのために5.2入れる甲斐がありそう」「あ、今回の5.2.1からじゃなくて5.2で既に入ってたのか😲」「そうそう、もう大手を振って使えます😋ある程度以上大きなプロジェクトならたいてい生SQL書くことになるので」

確かに5.2で入ってました↓。5.1ではClassMethodsが丸ごとprivateになっていました。

# https://github.com/rails/rails/blob/5-2-0/activerecord/lib/active_record/sanitization.rb#L123
      def sanitize_sql_array(ary)
        statement, *values = ary
        if values.first.is_a?(Hash) && /:\w+/.match?(statement)
          replace_named_bind_variables(statement, values.first)
        elsif statement.include?("?")
          replace_bind_variables(statement, values)
        elsif statement.blank?
          statement
        else
          statement % values.collect { |value| connection.quote_string(value.to_s) }
        end
      end

      private

参考: Ruby on Rails 5.2 / ActiveRecord::Sanitization::ClassMethods — DevDocs
参考: SQLインジェクション - Wikipedia

「今までprivateだったのは、もしかすると『生SQL書くな』ってことなのかな?なんて思ったりもするけど」

「話逸れますけど、@__gfx__さんのTwitter IDの前後にアンスコが2つずつついてるので、この人のツイートはTechRachoの記事に直接埋め込めないんです😭」「Markdownと誤認識されちゃうのね: ワカルワカル😆」「RailsdmでElasticsearchの話してた方でしたね↓」

「SQL向けのpublicなsanitizeメソッドって他にあった気がしないでもないけど気のせいだったかな🤔…?」

ActiveRecord::Base.sanitizeが以前あったのが削除されたそうです↓。placeholder展開もしないようです。
* ActiveRecord::Base.sanitize removed in 5.1 · Issue #28947 · rails/rails

追いかけボイス: 「(後日)sanitize_sql_arrayあまり使わないと聞きましたが」「Arelは基本使わないし、生SQLも本当に必要になるまで使わないマンなので🤓」「Rails wayというかActiveRecord wayでやってるんですね☺」「生SQLは最近少し使ったのでそのときはさすがにsanitize_sql_arrayしましたが😎

⚓Relation#updateのスコープ追加回避」を取り消し

5.2.1rc1から5.2.1の唯一の変更点でした。

# activerecord/lib/active_record/persistence.rb#L100
-      def update(id, attributes)
+      def update(id = :all, attributes)
        if id.is_a?(Array)
          id.map { |one_id| find(one_id) }.each_with_index { |object, idx|
            object.update(attributes[idx])
          }
+        elsif id == :all
+          all.each { |record| record.update(attributes) }
        else
          if ActiveRecord::Base === id
            raise ArgumentError,
               "You are passing an instance of ActiveRecord::Base to `update`. " \
               "Please pass the id of the object by calling `.id`."
           end
           object = find(id)
           object.update(attributes)
           object
         end
       end

つっつきボイス: 「all.each { |record| record.update(attributes) }のところeachで回してるとは😲あ、でもeachでやらないとフックが実行されないのか: でもパフォーマンスが問題になりそうな気もするけど🤔」「breaking changesになるのでいったん取り消したみたいですね」「確かにこれはbreaking changesになりますね: ARの#updateのフックをざっと見てみると、before_validationとかbefore_saveなんかも呼ばれてるので、breaking changesだけどこの取り消し前の修正が正しいんだな💡」「😃

参考: Railsのcallbackについて調べた - Qiita

「元の修正は0b29a42か: それにしてもIDにarrayを渡せるとは知らなかったし」「まあ自分はRelation#updateはそんなに頻繁には使ってませんけどね😋

⚓DBのパラレルテストを高速化


同PRより

# activerecord/lib/active_record/tasks/database_tasks.rb#L248
      def load_schema(configuration, format = ActiveRecord::Base.schema_format, file = nil, environment = env, spec_name = "primary") # :nodoc:
        file ||= dump_filename(spec_name, format)
+         verbose_was, Migration.verbose = Migration.verbose, verbose? && ENV["VERBOSE"]
        check_schema_file(file)
        ActiveRecord::Base.establish_connection(configuration)
          case format
         when :ruby
           load(file)
         when :sql
           structure_load(configuration, file)
         else
           raise ArgumentError, "unknown format #{format.inspect}"
        end
        ActiveRecord::InternalMetadata.create_table
        ActiveRecord::InternalMetadata[:environment] = environment
+      ensure
+        Migration.verbose = verbose_was
      end

つっつきボイス: 「ピンポイントで修正してますね」「これは見てのとおりマイグレーションを呼ばずにスキーマを読み込むように変わってるし: これなら確かに速くなる🏎

⚓ネストした複数のrespond_to同士に互換性がない場合にのみ例外を発生するようになった

# actionpack/lib/action_controller/metal/exceptions.rb#L64
+  class RespondToMismatchError < ActionControllerError
+    DEFAULT_MESSAGE = "respond_to was called multiple times and matched with conflicting formats in this action. Please note that you may only call respond_to and match on a single format per action."
+     def initialize(message = nil)
+      super(message || DEFAULT_MESSAGE)
+    end
+  end

つっつきボイス: 「今までのだとouterとinnerが違っても通っちゃってたのか↓: jsなのに中にHTMLがあったらそりゃマズイ」

# 同PRより
    respond_to do |outer_type|
      outer_type.js do
        respond_to do |inner_type|
          inner_type.html { render body: "HTML" }
        end
      end
    end

「でRespondToMismatchErrorクラスを新しく定義してrespond_toでraiseするようになったと↓: respond_toってネストできるのね💡

# actionpack/lib/action_controller/metal/mime_responds.rb#L193
     def respond_to(*mimes)
       raise ArgumentError, "respond_to takes either types or a block, never both" if mimes.any? && block_given?

       collector = Collector.new(mimes, request.variant)
      yield collector if block_given?
       if format = collector.negotiate_format(request)
+        if content_type && content_type != format
+          raise ActionController::RespondToMismatchError
+        end
        _process_format(format)
        _set_rendered_content_type format
        response = collector.response
         response.call if response
       else
         raise ActionController::UnknownFormat
       end
     end

⚓HEADのデフォルトのContent-Typetext/html

# actionpack/lib/action_controller/metal/head.rb#L38
      if include_content?(response_code)
-        self.content_type = content_type || (Mime[formats.first] if formats)
+        self.content_type = content_type || (Mime[formats.first] if formats) || Mime[:html]
        response.charset = false
      end

つっつきボイス: 「今までHEADが返してたのはMime::NullType😲」「HEADはHTTPのメソッドですね」「あーなるほど、HEAD 200↓のときにMime::NullTypeを返すのは確かにどこかヘンだ: Webサーバーならtext/htmlを返すのが普通だと思うし」

# actionpack/test/controller/render_test.rb#L230
+  def head_default_content_type
+    # simulating path like "/1.foobar"
+    request.formats = []
+     respond_to do |format|
+      format.any { head 200 }
+    end
+  end

「たぶんですけど、修正前だとヘルスチェックで引っかかるんじゃないかな」「HEADって本文がないレスポンスでしたっけ」「お、RFC2616の14.17によるとContent-TypeGETリクエストと同じものを返すとある↓から、Mime::NullTypeを返すのは仕様違反だったっぽい」「おー😲」「もちろん常にtext/htmlを返していいものではないので適切なものを返さないといけないはずですが、少なくともデフォルト値としてはtext/htmlの方が適切でしょうね🧐」「なるほどっ😃

The Content-Type entity-header field indicates the media type of the entity-body sent to the recipient or, in the case of the HEAD method, the media type that would have been sent had the request been a GET.
HTTP/1.1: Header Field Definitions 14.17より

⚓ログ出力されるIPアドレスをプロキシのIPからリモートIPに修正

# railties/lib/rails/rack/logger.rb#L48
         # Started GET "/session/new" for 127.0.0.1 at 2012-09-26 14:51:42 -0700
         def started_request_message(request) # :doc:
          'Started %s "%s" for %s at %s' % [
            request.request_method,
            request.filtered_path,
-            request.ip,
+            request.remote_ip,
            Time.now.to_default_s ]
        end

つっつきボイス: 「おおー!この修正はかなりエライ!💪そしてbreaking changeなんだけど、これがbreaking changeになるような設定はよくないという意味でこの修正は正しい」「というと?」「修正前だとロードバランサーを間に挟んだときにロードバランサーのIPが取れてしまうから」「これってbreaking changeになるんでしょうか?ログの項目が変わっただけなのかなと思ったり」「いえいえ、修正前にこれでロードバランサーのIPが取れると思ってそれを当てにして実装していた人たちにとってはbreaking changeになるということです: 修正後が正しいのは確かですが🧐

参考: remote_addrとかx-forwarded-forとかx-real-ipとか - Carpe Diem

⚓ソート順変更をいったん取り消し

例のlimit(1).first問題の修正(#24131)の影響が大きいので、deprecationサイクルをはさんで行うことにしたそうです。

# activerecord/lib/active_record/relation/finder_methods.rb#L552
      def ordered_relation
-        if order_values.empty? && primary_key && limit_value.blank?
+        if order_values.empty? && primary_key
          order(arel_attribute(primary_key).asc)
        else
          self
         end
       end

つっつきボイス: 「ああこの間limit(1).firstですね: deprecationを挟むはもっともだし、こういうPRを見るとRailsのdeprecation warningにはちゃんと対応しないといけないなって改めて思いますね😎

⚓Rails

⚓Rails 5.2のcredentialチートシート(Ruby Weeklyより)

# 同記事より
Rails.application.credentials.secret_key_base

Rails.application.credentials.fetch(:secret_key_base) { raise "it seems you didn't configure credentials" }

Rails.application.credentials[:secret_key_base] || "someDefaultValue"

ENV["SECRET_KEY_BASE"] ||  Rails.application.credentials[:secret_key_base]

Rails.application.credentials.dig :secret_key_base

つっつきボイス: 「例のRAILS_MASTER_KEYを使うヤツっすね: 前も話しましたけど個人的にはあまり好きでない方式」「そうでしたね☺」「リポジトリよりはKMSとかに保存したいし、この方式だとマスターキーを持っている人じゃないとviで開けないし、そもそもエディタで開いて編集するのもちょっとねー😅」「確かにー」「ま、でも個人がHerokuで動かす小規模なアプリならこの方法で十分だと思いますけどね」「小規模チームにもよさそう」

「ところで記事タイトルのCheat Cheatってスペルミスなのかわざとなのかちょっと謎でした🤔」「😆

↓Urban Dictionaryに一応ありました。ダジャレのようです。

参考: Urban Dictionary: Cheat Cheat

⚓Hanami v1.3.0.beta1リリース

変更点:

  • デフォルトのフレームワークがRSpecに
  • Hanami::Utils::StringHanami::Utils::HashHanami::Utils::Inflectorが非推奨に: 一部はdry-inflectorに移行
  • body_parsersがミドルウェアに移行
  • HTTPSがRackミドルウェアに移行
  • アセットプリコンパイル時にディレクトリ構造を保持
  • ネストしたモジュール/クラス定義でactions/views/mailersを生成
  • CLIの機能追加

以下で試せます。

gem install hanami --pre
hanami new bookshelf

Hanamiは以前はLotusという名前だったことをついでに知りました。


つっつきボイス: 「RSpecに変わるのね☺ユーザー数多いし」「HTTPSをRackミドルウェアに任せるんだそうです」「アプリ側ではSSLしないよなーやっぱり」

後で検索してみると、記事中で触れられていたrack-sslミドルウェアはアーカイブ化されてたので、今だとrack-ssl-enforcerミドルウェアを使うんでしょうね。

⚓Dev.toがRailsアプリのソースコードを公開

フロントエンドはPreactjs、単体テストはJestだそうです。


preactjs.comより


つっつきボイス: 「社内Slackで教わりました」「Dev.toって本当にRailsだったのね😳」「爆速で有名になりましたね: CDNも使いまくってるんでしょうね」「CDNは当然使ったうえで他にもいろんな最適化やってるはず: CDNだけであんなに速くなるはずないので🧐」「コミット数500そこそこは意外に少ないかも」「お、コントローラでset_surrogate_key_header "now_page"みたいにサロゲートキー使ってる↓👀」「何でしょう?」「ちゃんと見ないと何とも言えないけど、ハッシュ的なものに格納してるっぽいので、おそらくこの辺は固定ページなのかな」「最適化されまくったソースを参考にRailsアプリを書くのはちょっと大変そうだし、Railsで超高速を目指すのは果たしてどうなのかとは思いつつも、これは面白いですね😋今度中身見てみよっと🛠

# 同リポジトリより
class PagesController < ApplicationController
  # No authorization required for entirely public controller
  before_action :set_cache_control_headers, only: %i[rlyweb now events membership survey]

  def now
    set_surrogate_key_header "now_page"
  end

  def survey
    set_surrogate_key_header "survey_page"
  end

  def about
    set_surrogate_key_header "about_page"
  end


dev.toより

⚓Inspec: インフラのテスト/監査フレームワーク(Ruby Weeklyより)


inspec.ioより


つっつきボイス: 「RSpecのインフラ版みたいなやつが前からありましたけど…えっと何でしたっけ」「serverspec」「それそれ、それのライバル的なものみたいです」


serverspec.orgより

「↓この辺はserverspecっぽい」

# inspec.ioより
describe file('/etc/myapp.conf') do
  it { should exist }
  its('mode') { should cmp 0644 }
end

describe apache_conf do
  its('Listen') { should cmp 8080 }
end

describe port(8080) do
  it { should be_listening }
end

「↓これはAWSのリソースチェックか: だいたいやってることは同じかな😎

# inspec.ioより
describe aws_s3_bucket(bucket_name: 'my_secret_files') do
  it { should exist }
  it { should_not be_public }
end

describe aws_iam_user(username: 'test_user') do
  it { should have_mfa_enabled }
  it { should_not have_console_password }
end

参考: InSpecではじめるテスト駆動インフラ - tkak’s tech blog

「そういえばInspecのロゴの下にちっちゃーくChefって書いてありますね↑」「うんChefっぽい」


chef.ioより

「最近Chefってあまり聞かなくなった気がしますけどどうなんでしょう?」「Chefは何年か前に流行りましたが結局オーバースペックでしたね: Chefに精通したインフラエンジニアがいないと結局つらくなる」「😲」「そういえばBPS社内でもAnsibleに移り変わってますね」「chef-soloしか使わないならAnsibleでいいんじゃね?って思うし: Ansibleはシンプルだし実行順序が保証されるので」「ということは…」「Chefは実行順序が保証されないところが大変: イミュータブルなコンフィグレーションだと思って書かないとつらい」「あー」「実行順序を保証するときにはChefからシェルスクリプト呼んだりとか」「それって本末転倒感💦


ansible.comより

Itamaeは?」「クックパッドさんの作ったヤツで、こちらは使いやすいです❤


itamae.kitchenより

「あとChefにいいrecipeがあまりなかったのが残念」「うーむ」「今はchef.ioだけど確か元々違う会社名でChefのホスティングサービスをやってたはず…何だっけ…(しばらく探す)↓Opscodeだっっっ!🎯」「しかも2013年に社名変わってたんですね」「というぐらい最近Chefを使ってなかった😅

参考: Chef開発元のOpscode、社名をChefに変更。「検索が難しくなる」とあちこちで悲鳴が - Publickey

chefからansibleに乗り換えた5つの理由

⚓番外: gist-it: GitHubリポジトリをGist的に埋め込めるサービス


つっつきボイス: 「Railsとは関係ないんですが、GistでなくてもブログにGitHub上のコードを埋め込めるというので後で試してみます」「😃

できました↓。?slice=12:18のようにパラメータを追加すれば行数を指定できます。

diffは無理みたい(´・ω・`)。

⚓Ruby trunkより

⚓提案: Hash#===Array#===Ruby Weeklyより)

他にFeature #14973: Proposal of percent literal to expand Hash - Ruby trunk - Ruby Issue Tracking Systemもありました。

珍しくRuby Weeklyがtrunkのissueを紹介していたのですが、いずれも日本語で書かれています。

Hash#===
レシーバのキーの要素と引数のハッシュのキーの要素を #=== で比較して、全てが真なら true を返し、そうでないなら false を返す。
また、レシーバが空のハッシュの場合、引数が空のハッシュなら true を返し、そうでないなら false を返す。

user = { id: 1, name: "homu", age: 14 }

# name 要素が data にあるので true
p ({ name: "homu" } === user)
# => true

# 複数の要素があっても OK
p ({ id: 1, name: "homu", age: 14 } === user)
# => true

Array#===
配列の各要素をそれぞれ順に === で比較し、全要素が true の場合に true を返す。そうでない場合は false を返す。

# 配列の各要素を #=== を使用して比較する
[ String, /\w/ ]          === [ "a", "c", 7 ]   # => false
[ String, /\w/, (1..10) ] === [ "a", "c", 7 ]   # => true
[ String, /\w/, (1..10) ] === [ "a", "!", 42 ]  # => false

つっつきボイス: 「さっきbugs.ruby-lang.orgが落ちてたんですが今は動いてるのでやっと読めました💦」「issueは日本語だけど英語のレスもついているという😆」「トリプルイコールかー😲」「ディープな場合はどうなるんだろう?」「これは要素を順に比較するだけだからディープな動作ではないですね😎」「こういうのを見ると、メソッドの戻り値が複数の場合に===でパターンマッチ的に比較したくなりますね」

「今は===ってどこにあるんだっけ?(コードを掘り始める)」「ownerで取れますね確か」「あった: Kernel#===なのね↓」「普段めったに使わない操作w」「methodsでやるとドバっとメソッドが表示されるから大変ですよね」

[].method(:'===').owner #=> Kernel

RubyのIRBやpryでメソッドの定義元をすっと調べる方法

Hash#===Array#===、あったらうれしいという気持ちはわからなくもないけど」「===だからcase文で使えるってことなんですね」「これかー↓」「これは相当しっかり理解しとかないとハマりそう😅」「このcase文はヤバイ😆」「Rubyのcase文はもともとかなり自由だし」

#14916より
def plus *args
  case args
  # 数値の場合
  when [Integer, Integer]
    args[0] + args[1]
  # 数字の場合
  when [/^\d+$/, /^\d+$/]
    args[0].to_i + args[1].to_i
  # それ以外はエラー
  else
    raise "Error"
  end
end

p plus 1, 2
# => 3
p plus "3", "4"
# => 7
p plus "homu", "mado"
# Error (RuntimeError)

⚓Solarisでコケるテストがある


つっつきボイス: 「Solarisという単語につい惹かれてしまいました」「Ruby、Solarisサポートしてるのか😲」「そういえばSolaris使ったことあります?」「大学にあった気がする」「前職でちょっぴり」「薄紫色のピザボックス型でした」「筐体はいろいろありますからね: Fireには冷蔵庫みたいなデカイのもあったし」「マシン自体は見たことなかった…」「自分Ultra SPARC IIIのマシン自宅に持ってました💪」「おほー!」「DECのAlpha 21264マシンも持ってたし: どっちも捨てちゃったかな…」「それもスゲー」「64ビットマシンだったし: 今は珍しくも何ともないという🤣」「🤣」「あっ、でもAlpha 21264はRISCだったし: RISCは珍しーだろー🤓

参考: Solaris - Wikipedia
参考: Alpha 21264 - Wikipedia
参考: RISC - Wikipedia

⚓提案: Any

# issueより
class Any
  class << self
    def ===(b)
      true
    end

    def ==(b)
      true
    end

    def to_proc
      proc { true }
    end
  end
end

# ------

case ['Foo', 25]
when [/^F/, Any] then true
else false
end
# => true

case {id: 1, name: 'foo', age: 42}
when {id: Any, name: /^f/, age: Any} then true
else false
end
# => true

case {id: 1, name: 'foo'}
when {id: Any, name: /^f/, age: Any} then true
else false
end
# => false

先のArray#===Hash#===と組み合わせるといい感じになるという提案でした。


つっつきボイス: 「お、Any型かー: こういう番兵的なものが言語レベルであると実は便利なんですよね❤」「確かにー」「Scala言語にAnyってのがあるのね」「番兵って…? 英語だとsentinelって言うみたいですが…」「よくC言語なんかで文字列終端を表すヌル文字\0が番兵って呼ばれてますね: ただヌルって他でも使うから、たまたま値がヌルになるとそこで止まっちゃうなんてことがよくあるという」「なるほどー!😃」「だからこのAnyみたいに他では絶対使われない値があれば、終端が事前にわからないストリームなんかを扱う場合の番兵として使いやすいですよね☺

参考: Scala - Wikipedia
参考: 番兵 - Wikipedia

「提案しているのはRubyと関数型の記事でお馴染みのBrandon Weaverさんでした」

Rubyで関数型プログラミング#1: ステート(翻訳)

⚓Ruby

⚓Rubyの高速化は単純には見積もれない(Ruby Weeklyより)

Noah Gibbsさんの記事です。


つっつきボイス: 「パフォーマンスの見積もり方というか、個別の高速化を足しても100%の高速化にはならないよ的な話のようです」「ぼやきというか」「高速化は単純に足し算できませんからね🕶」「Noah Gibbsさんがチューニングしたものをさらに高速化するのはすげー大変そう」「自分ならマシンを速くして高速化するな🤣」「札束で殴る🤣

⚓undercover: 賢いカバレッジ(Ruby Weeklyより)


同リポジトリより


つっつきボイス: 「カバレッジに対するRuboCopみたいなものだそうです」「おー、lambdaとかの中までチェックしているみたい」「全部のクラスにはかけたくはないけど、決済系のコードみたいなすごく重要な部分に絞ってこういうツールをかけるのはありかも😋」「ありそうでなかったツールなんですね😃」「機会があったらピンポイントで使ってみようかな」

⚓STDOUT/STDERRデバッグメッセージの出処を突き止める

# 同記事より
class << STDERR
  alias_method :orig_write, :write
  def write(x)
    orig_write(caller[0..3].join("\n"))
    orig_write(x)
  end
end

つっつきボイス: 「Sam Saffronさんの記事です」「ははぁこうやってSTDERRを拡張するのね↑」「おほー😃」「STDERRってクラスじゃないですよね?」「RubyではIOオブジェクトだったはず: Rubyはオブジェクトを直接拡張できるので、そこにalias_methodwriteをフックすると」「やるな~」「これは実にRubyらしい発想❤

参考: constant Object::STDOUT (Ruby 2.5.0)
参考: class IO (Ruby 2.5.0)

「他の言語だとどんな感じになるんでしょうか?」「他の言語だと、普通はSTDOUTやSTDERRのファイルディスクリプタが取れるので、ファイルディスクリプタをウォッチするOSのメソッド(macOSだとFSEvents、Linuxだとinotifyあたりだったかな)を使って、特定のディスクリプタに対して特定のAPI呼び出しをハンドルする感じですかね」「なるほどー😃」「例のguard gemみたいなファイル更新をチェックするソフトウェアは、たぶんそのあたりを使ってると思いますよ」「他の言語だとOS側でイベントを監視してもらわないといけないんですね」「ですです: 普通はOSにどこかでイベントをディスパッチしてもらわないといけない: でもRubyだと上のようにSTDOUTやSTDERRといったオブジェクトに直接フックをかけられるのが面白いですね😋

参考: ファイル記述子 - Wikipedia

そういえばGobyでたまたまSTDOUT.to_sしたらそのまんま<File: /dev/stdout>が出てきたのを思い出しました😅。もろにファイルディスクリプタ。

⚓Rubyの整数の最適化

参考: 第2章 オブジェクト — Rubyソースコード完全解説


つっつきボイス: 「RubyのRVALUEの最適化ってほんと半端ない」「RVALUEは記事を翻訳して知りました↓」「ツイートで『ポインタを, 何らかの構造体へのポインタとして使ったり, 整数そのものとして扱ったりすることを褒めたつもりだった』ってありますね」「最適化では割と使われる手法だけど、自分はちょっとビビる😅

Rubyのメモリ割り当て方法とcopy-on-writeの限界(翻訳)

/* http://i.loveruby.net/ja/rhg/book/object.html より*/
 123  #define INT2FIX(i) ((VALUE)(((long)(i))<<1 | FIXNUM_FLAG))
 122  #define FIXNUM_FLAG 0x01

(ruby.h)

「このFIXNUM_FLAG↑っていう発想が面白い: 下位1ビットをフラグとして使うことでFixnumかBignumかを判定するという😆」「それ完全にハックやん…🛠」「4の倍数アドレス云々の部分は確かPOSIXで規定されていたはずだから、mallocを使っていればポインタが4の倍数アドレスであることが保証されるはず」「仮に1ビット単位で最適化するソフトウェアがあったとしても、mallocを使えば4の倍数にアラインされる」「その隙間をフラグに使うという😲」「爪に火を灯すような最適化🔥

参考: POSIX - Wikipedia

⚓Julia Evansさんがプロファイラを語る


同PDFより


つっつきボイス: 「このPodcastの凄い点は、スポンサーを募って文字を全部書き起こしているところかも」「おータイムスタンプまできっちり起こしてるし↑😲この起こしは大変そう」「PDFですけどね😆」「他のPodcastなんかで、あちこち<UNREAD>みたいになってる文字起こしをちょくちょく見かけますけど、あれはきっと自動で起こしてるんだろうなーって🤣」「🤣」「🤣」「技術トークの起こしって音声認識泣かせですねホント」「実際に目の前で聞いてもよくわからないことあるし🤣

Ruby: eBPFでメモリアロケーションをプロファイリングする(翻訳)

⚓debug_helper: デバッグ情報をyaml風に出力するgem(Hacklinesより)

# 同記事より
require 'debug_helper'

ary = [0, 'one', :two]
DebugHelper.show(ary, 'My mixed array')
Array (message='My mixed array'):
  size: 3
  Element 0: Fixnum 0
  Element 1:
    String:
      to_s: one
      size: 3
      encoding: !ruby/encoding UTF-8
      ascii_only?: true
      bytesize: 3
  Element 2:
    Symbol:
      to_s: two
      size: 3
      encoding: !ruby/encoding US-ASCII

つっつきボイス: 「ほほーyamlに出力とな」「出力に改行が混じったりするとyamlもそんなに読みやすくないけど😅」「😆」「↑このぐらいならキレイでも現実のデータだとなかなかこうならなかったりしそうだけど、とりあえずこうやって作ってみたのはいいですね😋

⚓Rubyのオブジェクト生成方法を変えるには(Hacklinesより)

割と短い記事です。


つっつきボイス: 「モジュールでsuperしてるし↓」「なぬ?😅」「その後object.singleton_class.include(Logging)してるから動くけど、なかなかキモい👻

# 同記事より
module Logging
  def make_noise
    puts "Started making noise"
    super
    puts "Finished making noise"
  end
end

class Bird
  def make_noise
    puts "Chirp, chirp!"
  end
end

object = Bird.new
object.singleton_class.include(Logging)
object.make_noise
# Started making noise
# Chirp, chirp!
# Finished making noise

「最終的にself.newしてるし↓」「なぬなぬー?😅」「ははぁ、allocateをオーバーライドできないからこういう遠回りな方法でやってるのか」「おー😲」「確かにコアクラスの方でオーバーライドされると手も足も出なくなっちゃうからこういう手段に出たんだろうけど、基本的にはこういうことはしない方がいい気はするなー🧐」「STIなんかで下の方に埋め込まれてコントロールできなくなっちゃうとか、たまにありそうですね」「newを無理やりオーバーライドするか、さもなければリフレクションしまくるか、どっちもやりたくないなー😭」「この記事のやり方はモジュールをincludeで入れているあたりがDIっぽいからまだいい方かも😋

# 同記事より
class Bird
  def self.new(*arguments, &block)
    instance = allocate
    instance.singleton_class.include(Logging)
    instance.send(:initialize, *arguments, &block)
    instance
  end
end

参考: 依存性の注入 - Wikipedia

⚓mini_i18n: 小規模なi18n化ライブラリ

# 同リポジトリより
>> MiniI18n.t(:hello)
=> "Hello"
>> MiniI18n.t(:hello, locale: :fr)
=> "Bonjour"
>> MiniI18n.locale = :fr
=> :fr
>> MiniI18n.t(:hello)
=> "Bonjour"
>> MiniI18n.t(:hellooo)
=> nil

つっつきボイス: 「文字どおりミニなi18n☺
「ところでRailsのi18nって基本的にRubyのI18nだから別に大きくはないですよね?」「お、でも#l#localizeのエイリアス)とか割と複雑なことやってた気がしたけど: DateTimeとかを渡すとその国の形式に変換してくれるヤツ」「あれってRubyのI18nに丸投げしてませんでしたっけ?」「あーrequire i18nしてるからやっぱりRubyのI18nですね」「でもエイリアスはRailsの方で定義されてるみたい…」「ややこしい😅

# File actionview/lib/action_view/helpers/translation_helper.rb, line 117
def localize(*args)
  I18n.localize(*args)
end

参考: Ruby on Rails 5.2 / ActionView::Helpers::TranslationHelper#localize — DevDocs

「そういえばRailsの#lメソッドってnil渡すとエラーになるんですよね😅」「そうそうw 誰もが一度はハマるヤツ😆」「でpresent?するとその後if文がだんだん増えていくという🤣


「ちょっと話逸れるんですけど、10年くらい前に英語/日本語を同じぐらい使える大先輩に『ローカライズの仕事してます』って話したら大爆笑されたことがあって、どうやらlocalizeっていう単語は技術から離れると『土着化』とか『現地住民と同化する』的に響くところがあるみたいで」「😆」「もともとそういうニュアンスで使われてた時代があったからかも🤔」「そのせいかどうかわかりませんが、最近はlocalizeという言葉をglobalizeと言い換えてるところもあるようです☺

localizeの基本的な意味は「局所化」ですね。

参考: ローカライゼーション - Wikipedia

⚓その他Ruby



↑シンガポールでの講演だそうです。

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

⚓Google Cloud MemorystoreのRedisサービス

最初の比較記事を書いたときはCloud Memorystoreはまだなかったそうです。

⚓GLB: GitHubのロードバランサー


同記事より


つっつきボイス: 「GLBはまだざっとしか見てなくて正体わかってないんですが、この図↓などを見た感じではNGINX Unitにちょっと似ている印象でした」「とりあえずロゴ↑は秀逸ですね😋」「カワイイ❤

新しいRubyアプリサーバー「NGINX Unit」を調べてみた(翻訳)

⚓Statsd: 統計収集ツール

Node.jsベースで書かれているようです。★13,000超えです。


つっつきボイス: 「Sam Saffronさんの記事に登場してたので」「めっちゃ★付いてる」「これは使ったことなかった」

参考: netdata と statsd によるリアルタイムモニタリング - biaxident’s blog

⚓標準電波JJYの夏時間対応


つっつきボイス: 「JJY、夏時間は対応してるけど2時間ずらしは対応してないという😆

⚓サーバーレスアプリのアーキテクチャとパターン(Serverless Statusより)


つっつきボイス: 「Microsoftのドキュメント、例によって最近まとまりがいいですね😋」「日本語版もあります↓」

参考: サーバーレス アプリ: アーキテクチャ、パターン、および Azure の実装 | Microsoft Docs

⚓Google App Engineの次世代ランタイムとPython 3.7

Introducing App Engine Second Generation runtimes and Python 3.7 | Google Cloud Blog

よし、これでpython2を捨てれる

2018/08/09 12:23


つっつきボイス: 「個人的におおっと思ったので: 以前のApp EngineライブラリはPython 2系で、機械学習系ライブラリは3系が多かったので、自分のMacbookでpyenvで切り替えないといけなくて何かと面倒でした」「今までPython 3系使えなかったとは😲」「GoogleってPythonの偉い人がいるはずなんだけどなー: でももしかするとその人が2系を守ってたとか、2系に寄せたチューニングをやりすぎてたとかだったりして🤣」「🤣

⚓モバイル/Android/iOS

⚓Flutter対React Native(Mobile Dev Weeklyより)


同記事より


つっつきボイス: 「Flutterの方が前からあったというのが意外でした」「まあ検索ボリュームって意外と当てになりませんけどねっ😎: みんなが当たり前に使うようになるとかえって検索数減ったりとか」「😆

⚓SQL

⚓サイボウズさんのMySQLのパフォーマンスチューニング


つっつきボイス: 「お、はてブでも上がってましたねこの記事: JOIN禁止とか書いてないところがエライ😆」「😆」「JOIN禁止ってソシャゲ界だとありがちなんですよね」「そんなレベルでパフォーマンスを気にしてるんですかね?」「ソシャゲだとシャーディングしてたりするから」「あー😲

ハンズオン: PostgreSQLシャーディング(翻訳)

「ちなみに昔のMySQLはJOINが遅かったし、今でもサブクエリは遅いという😆」「それでもMySQLのサブクエリは以前の異常な遅さよりはずいぶんマシになったというか順当な遅さ(😆)にとどまるようになったみたいですね」「以前があまりに遅すぎた🐢」「そんなに!?」「サブクエリをWHEREで書き直すだけで1000倍以上速くなったりしてましたし🏁: おかげでサブクエリをWHEREで書き直す方法を覚えたけど、今度はPostgreSQLでサブクエリ使うのを最初ためらっちゃいましたね🤣」「サブクエリの方が読みやすいのは確か」「ほんに」

参考: 漢(オトコ)のコンピュータ道: なぜMySQLのサブクエリは遅いのか。 — 2009年の記事です

⚓AWS AuroraとAWS RDSの使い分け(DB Weeklyより)


percona.comより


つっつきボイス: 「これはPerconaさんの記事ですね」「Perconaは元々MySQLのチューニングツールを出してた会社ですが、その後MariaDBのカスタマイズ版のPerconaDBを出したりしてます」

参考: MariaDB - Wikipedia
参考: What is Percona DB | InMotion Hosting

「AuroraがPostgreSQLでしたっけ?」「いや、Auroraは元々MySQL互換で、その後for PostgreSQLが出た: まだベータだったかな?と思ったらもうベータは取れてるのか」「でRDSが…」「MySQL、PostgreSQL、Microsoft SQL Server、MariaDB、Oracle、それとAuroraですね」「そういう構成だったんですね😃

「ただAuroraはノード数がある程度必要なので結構お金かかります💰」「スケーラビリティとか凄そうですもんね」「あとAuroraが面白いのがストレージを使えば使うほど速くなるところ😆」「😆」「どういう仕組みなんでしょう?」「分散がデータブロック単位になっていて、データ量が増えるとデータブロックも分散して並列化が進むので速くなる」「おほー😲」「なのでデータ量が増えても指数関数的に遅くならないという」「すげー」

「Auroraのコアって実は分散ファイルシステムになっていて、他にも書き込みの非同期ネゴシエーションで『n個以上のノードからACKが返ってきたら書き込み成功と見なして進む』(Asynchronous group commits)ようになってるとかすごく頑張ってる、という解説を2、3年前のAWSサミットで聞いてしみじみ感心しました」「😃」「それをMySQLとかPostgreSQLとかと別のところでここまで追い込んでいるのが凄すぎ」「昔は研究レベルだったことが今こうやって使えますからねー☺

参考: Amazon Aurora(MySQL、PostgreSQL 互換のリレーショナルデータベース)|AWS
参考: 無料でデータベースを構築する

⚓TimescaleDBとInfluxDBを比較(Postgres Weeklyより)


同記事より


つっつきボイス: 「InfluxDBはNoSQL系だそうです」「Fluxってどこかで聞いた…あ、時系列データベースか」「Timescale社のブログなので最後はSQLに軍配を上げつつ自社のTimescaleDBを推してます☺


influxdata.comより

参考: Flux | Application Architecture for Building User Interfaces
参考: Influx Query Language (InfluxQL) reference | InfluxData Documentation


timescale.comより

⚓JavaScript

⚓Reduxの「Reselect」ライブラリでメモ化

JavaScript: Reduxが必要なとき/不要なとき(翻訳)

⚓React/Preact/VueでサーバーサイドレンダリングのXSSを修正(JSer.infoより)


つっつきボイス: 「Preactって名前だけどこかで見たんですけど何でしたっけ?」「リポジトリにFast 3kB React alternativeって書いてあるし」「速いReact」「もしかしたらFacebookのライセンスを気にしなくていいとか?」

後で「Facebook、ReactをMITライセンスに変更」という記事を見つけました。


github.com/developit/preactより

参考: Using Preact as a React Alternative — SitePoint

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

⚓EV証明書

Firefoxを自分でビルドしてオレオレEV証明書を使えるようにしてみたそうです。「再定義できない世界になりつつあるネットの新秩序 - 雑種路線でいこう」という記事で知りました。

⚓JavaScriptの「Reactivity」を理解する(Frontend Weeklyより)


同記事より


つっつきボイス: 「Reactivity: また新しいものが」「DOM構造的にreactできるかどうかという話かな?」「時間ないので次へー」

⚓言語よろずの間

⚓Inko: RubyでコンパイルしてRust VMが走る言語

GobyのメンバーにもInkoに興味津々の人がいました。

⚓chart: 標準出力からいきなりグラフを生成(Golang Weeklyより)

Go言語で書かれています。早速インストールしました

# 同リポジトリより
history | awk '{print $2}' | chart


同リポジトリより


つっつきボイス: 「みんな一度はこういうの作る☺」「☺

⚓その他

⚓G.ワインバーグ氏死去

↓だいぶ前にこの本で知りました。

参考: ジェラルド・ワインバーグ - Wikipedia — ワインバーグの法則がどっちゃり載っていてちょっとびっくりしました。

⚓名著復活の兆し?

少し違いますがこんな記事も↓。


つっつきボイス: 「売れない理工学書はそもそも売れないから運営資金が続かないという問題がつきまといますね…」「😭

参考: 科学論文の海賊版「Sci-Hub」を違法と訴える巨大出版社に対して根本的法改正の必要性を訴える科学者という構図 - GIGAZINE

⚓【書評】SOFT SKILLS


つっつきボイス: 「SOFT SKILLSはどうやら評価が両極端に分かれてますね」「身体を鍛える話とか載ってるみたい」「このあたりは人に言われてやるのとセルフモチベーテッドでは相当違ってきそう🤔

⚓技術書オンリーイベント第5回が10/08(月)開催決定


つっつきボイス: 「今度こそ行きたいと思って😤

⚓番外

⚓Dimensions: 数理を動画で学ぶ

フランスで制作された、割と前からあるCreative Commonsベースのコンテンツで、個人的に好きです。最初に見たのはニコ動でした。


つっつきボイス: 「上の日本語版動画は東大のサイトにあります」「おwフォーマットがRealVideoとかWMVとか😆」「かなり歴史を感じるフォーマット📜

参考: RealVideo - Wikipedia
参考: Windows Media Video - Wikipedia


今回は以上です。

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

週刊Railsウォッチ(20180806)Rails 5.2.1.rc1リリース、Railsガイド日本語版が5.1に対応、Regexp#match?ほか

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

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

Rails公式ニュース

Ruby Weekly

Hacklines

Hacklines

Postgres Weekly

postgres_weekly_banner

DB Weekly

db_weekly_banner

Serverless Status

serverless_status_banner

Mobile Dev Weekly

mobile_dev_weekly_banner

Frontend Weekly

frontendweekly_banner_captured

JSer.info

jser.info_logo_captured

Golang Weekly

golangweekly_logo_captured

mrubyをWebAssemblyで動かす(翻訳)

$
0
0

概要

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

日本語タイトルは内容に即したものにしました。

mrubyをWebAssemblyで動かす(翻訳)

要約: WebAssemblyがやってきました!やってみたい方は本記事のwasm gemをお試しください

2018年の世界へようこそ。最も興奮を呼び起こすイノベーションがコンパイラ界隈にも巻き起こりつつあります。ARやVRや機械学習など先端を行く話題はいくつもありますが、ソースコードを機械が実際に理解できる形に変換する役割を担うコンパイラの歴史に比べればまだまだ新顔です。コンパイラの復権は主にLLVMのおかげです(LLVMはモジュール形式のコンパイラインフラストラクチャプロジェクトです)。LLVMの重要性については、Wiredの2013年の記事に的確な指摘がまとまっています。

その希望とは、LLVMがソフトウェア開発の新時代の水先案内人となり、アプリケーションがあるマシンから別のマシンに、そしてあるプロセッサから別のプロセッサにすら自由に移動できるようになることだ。

以来5年が経過し、これはかなりの部分まで実現しました。LLVMの導入とそれがもたらすモジュラーパラダイムによって、ソフトウェア開発が根底から変革されました。LLVMによって、移植性/最適化/言語実装/コンパイルライフサイクル全体がラディカルに考え直されたのです。詳しく知りたい方はoverview of LLVMをご覧ください。著者のChris LattnerはLLVMの主要な作者であり、しかも Swiftの創立者でもあるのです。Chrisの昨年のインタビューをAccidental Tech Podcastで聞くことができます。

LLVMがWebといったい何の関係があるのでしょうか?Webのプログラミングといえば、その草創期(1995年)からJavaScriptと相場が決まっていました。JavaScriptはその後23年間批判と論争の嵐をくぐり抜け、今もWebプログラミング言語として居座っています。JavaScriptを置き換える強力なライバルが不在であったにもかかわらず、言語としてよい感じに成熟と進化を繰り返してWebに関わり続けています。Webブラウザも、静的コンテンツを表示するだけの単なるビューアから、数十億ものユーザーやシステムの基盤を追い求める企業の要求に後押しされたフル装備のアプリケーションプラットフォームへと着実に進化しています。しかしながら、ブラウザが「正しい」アプリケーションプラットフォームになるにはまだひとつ足りないものがあります。Wired誌の同じ記事から再度引用します。

JavaScriptの難点は、言語としては比較的シンプルであることだ。他の言語(Javaや、誉れ高きCやC++など)ならできることでもJavaScriptでできるとは限らない。私たちに必要なのは、アプリケーションをどんな言語で書いてもあらゆるマシンで動かせる方法だ。それも大きな妥協を強いられずに。そしてLLVMはそれを目指している。少なくともそう思っている人々がいる。

ブラウザがデスクトップOSやモバイルOSに匹敵するには、ブラウザに欠かせない例のあの言語からブラウザ自身が解放されなければなりません。つまりブラウザのプログラミング環境は、他のプラットフォームと同様もっと低レベルになるべきです。ブラウザは独自のアセンブリ言語を備え、特定のハードウェアに依存しないよう抽象化しつつ、マシンそのものにできるだけ近いところに配置されるべきです。2012年に始まったasm.jsプロジェクトも同じものを目指していました。asm.jsは、JavaScriptのサブセットを定義することで、コンパイラにとって理想的なターゲット言語になるべくJavaScriptのコンセプトを絞り込みます。ここにLLVMとasm.jsの歴史がひとつとなり、Alon Zakaiが生み出したLLVMからJavaScriptへのコンパイラであるEmscriptenというツールがすべてを可能にしました。今や私たちは、CやC++で書かれたプログラムを次のようにJavaScriptにコンパイルできます。

C/C++ → LLVM → Emscripten → JavaScript (asm.js)

詳しくは、Alonのスピーチ「Compiling to JavaScript」やCppCon 2014でのEmscriptenやasm.jsについてのスピーチ↓をご覧ください。

asm.jsの登場によってWebのアセンブリ言語が産声を上げたのです。これはあらゆるブラウザで標準化および統合化される可能性を秘め、そして実際に2015年のWebAssemblyのクロスブラウザ化作業の開始でまさにそれが起きたのです。この作業では、移植性と効率の高いバイナリインストラクション形式や安全な仮想マシン(VM)の定義をWeb向けにさらに進めます(Brendan Eichの“From ASM.JS to WebAssembly”も参照)。わずか2年の間にコミュニティは仕様を定めてプロトタイプをいくつも作り、Webプラットフォームに責任を持つ最大のあの企業と激論を戦わせ、主要な全ブラウザでWebAssemblyをリリースしたのです。これは誇張抜きで歴史に残る偉業です。おそらくブラウザは最初からこうあるべきだったのでしょう。ブラウザは低レベルのコンパイルターゲットを提供して、多くのプログラミン言語もWebでプログラミングできるようにし、既存のネイティブツールやライブラリやアプリも使えるようにすべきだったのです。しかし、ブラウザが今日のような地位を占めるようになると一体誰が予測できたでしょうか。とはいうものの、今や私たちがハッピーになれる道が開けてきましたし、そこに秘められた能力にはいやが上にも期待します。MozillaがWebAssemblyはゲームを変革すると考えています。やってみたい方はWebAssembly Studioをご覧ください。

RubyをWebに持ち込む

多くの人がWebで他のさまざまな言語を何年も使っていますが、そのためにはJavaScriptを使わないわけにいきません。その最もよく知られた例はCoffeeScriptでしょう。CoffeeScriptは2009年に始まり、JavaScriptにうんざりした人(またはJavaScriptに戻ってきた人)に別の選択肢をもたらしました。RubyやPythonに似た拡張文法で書くことができ、純粋なJavaScriptにトランスパイルされます。素晴らしいじゃないですか!それから何年もの間source-to-sourceコンパイラが全盛でした。JavaScriptにコンパイルされる言語の長大なリストを見ると何とも言えない気持ちになります。Rubyも例外ではありません。その中でも優れものはOpalです。

このsource-to-sourceアプローチには、効率やパフォーマンス、低レベル最適化の喪失などいくつも短所があります。JavaScriptを歪めてまで需要を満たそうとする必要がそもそもあるのかという根本的な批判もあります。WebAssembly登場以前であれば、その答えは単純そのもので、他に手段がないのだからそうするしかなかったのです。それが今や他の手段があるのです。この新しいコンパイルターゲットを利用して、実にさまざまな言語をWebプラットフォームに暖かく迎えようという動きが始まっています。他の言語たちは正確にはどんな形でWebにやってくるのでしょうか?CやC++などのネイティブ言語は既にLLVMへのコンパイルが可能ですし、LLVMのその他の実装(Rustなど)も、コンパイラツールチェインのターゲットをWebAssemblyに設定するという素直な手順でできます。Rubyのように、インタプリタやVMを自前で持つ動的言語だと少々難しくなります。

その理由を知るために、Rubyの主要な実装であるMRI(CRubyとも呼ばれます)を見ていくことにしましょう。どんなプログラミング言語であっても、構文の仕様よりはるかに多くのものが含まれています。そうしたものは、言語の実装や利用状況によっても定義されます。Rubyの場合は伝統的に、Unix的な環境に依存する各種ツールの総合的なエコシステムを意味し、ファイルシステム、コンパイラツールチェイン、標準ライブラリ、ネットワーク、その他OSが提供することが期待されるあらゆるものが含まれます。MRIすなわちCRubyはそれらを期待していますし、そのことは別に問題ありません。Rubyの使われ方、たとえばRailsアプリを実行するWebサーバーなどは、このモデルに完全に適応していて、その利点をすべて享受できます。

しかし、利用状況が変わるとそうはいかなくなります。たとえばRubyアプリをエンドユーザーに配布するなどです。期待するRubyインタプリタをローカルにインストールし、どのアプリにも正しいバージョンのgemを提供するとき、一般人と開発者と正規ユーザーを同じに扱うわけにはいきません。Rubyインタプリタを丸ごとアプリに仕込んでリリースするという回避方法もなくはありませんが、どう考えても現実的ではないでしょう。鼻で笑わないでください。一応実際に可能なのですから。Traveling Rubyは、Windows/Linux/macOS向けにRubyを同梱したアプリパッケージを作成するという印象的なプロジェクトです。Minqi PanによるRubyConf 2017の秀逸なトークでは、Rubyアプリを単一の実行ファイルに固め、メモリ上に仮想のファイルシステムを作成するなどの裏技を駆使して無理やり動かすというデモを披露しました(動画コード)。どんな技を使ったのか私にはわかりませんが、私の好きなアプリの例(かつRubyデスクトップアプリとして最も知られているアプリ)はSonic Piというライブコーディング向けミュージックシンセサイザーです。

「インタプリタ同梱」的なアプローチは使えるは使えますが、サイズが相当巨大になるなど、いろいろ制約があります。参考までに、Ruby 2.5.1をmacOSに(rbenv install 2.5.1で)新規インストールしたときの典型的なサイズは20.7 MB、1,396ファイルにのぼります。Traveling Rubyでこれをtarball圧縮すると6.7 MB(未圧縮19 MB)、そしてMinqiのプロジェクトだと40.8 MB(34.1 MB圧縮)です。Sonic PIのようなデスクトップアプリなら大したことはありませんが、Webではそうはいきません。昨今だとどこかのWebサイトは10 MBをプッシュすることもあるようですが(某NYTimes.comとか)、これはあんまりです。それにまだ私たちはアプリのコード量を計算に入れていません。ファイルサイズだけではなく、MRI/CRubyの起動もかなり重く、メモリのフットプリントも相当な量になります。

MRI/CRubyインタプリタをブラウザに丸ごと同梱することもできなくはないかもしれませんが、現実的とは思えませんし、そもそも必要ですらないでしょう。とにかく、Rubyの実装だけの問題ではないことは明らかです。もっと効率的で軽量なRubyがどこかにあれば、インタプリタの必要な部分だけを取り出して、管理可能な小さいバイナリにコンパイルできるのではないでしょうか。おお、そういえばそんなものがあるじゃないですか!

mrubyのあらまし

mrubyのことをご存知ない方は、この機会にぜひお見知りおきを。個人的には、Rubyコミュニティで今アツいのはmrubyだと思います。mrubyプロジェクトを率いるのはRubyの作者としてお馴染みのMatzその人です。mrubyプロジェクトは2012年に組み込み(eMbedded)のユースケースに適した言語の実装としてスタートしました。組み込みの分野ではメモリやストレージといったリソースが限られています。ウォッシュレットのプログラミングも組み込みのひとつです。mrubyは完全にモジュール化されているので、MRI/CRubyのような巨大なインタプリタのモノリスをインストールするのではなく、好きなだけカスタマイズできます。ファイルシステムやネットワークアクセスが不要なら取っ払ってしまえばよいのです。ユーザーにできることを制限したいならそれもできます。mrubyはあなた好みのインタプリタをビルドできるのです。「完全なRubyとは違う」という制約はあるものの、利用状況に応じてmruby言語の主要な実装から本質的なメリットを得られます。mrubyについて詳しく知りたい方には、Zachary ScottのMRuby記事(訳注: サイトは現在ありません)とZacharyによるコミッターDaniel Bovensiepenへのインタビューがおすすめです。

mrubyは、インタプリタのアーキテクチャを考え直すことで新たな可能性の扉をも開きました。そのひとつが今私たちが興味津々の「Rubyスクリプトをネイティブコードにコンパイルする機能」です。プログラムを配布するのにありがたい機能ですね。アプリを単一の小さなバイナリで作成できるということは、Rubyの移植性が著しく高まるということです。マシンを問わずらゆる主要なプラットフォームで動く単独の実行ファイルを簡単に生成するmruby-cliのようなプロジェクトもあります。mrubyのバイトコードはCのプログラムに直接埋め込むこともできますので、clang(C言語ファミリーのLLVMフロントエンド)でコンパイルできます。コンパイルは次のような流れになります。

Rubyスクリプト → mrubyバイトコード → C → clang → LLVM → ネイティブ実行ファイル

ここまでご理解いただけましたでしょうか?

mrubyからWebAssemblyまで

コンパイラのターゲットをLLVMにできるなら、WebAssemblyへの道が見えてきます。先ほどのEmscriptenがLLVM-to-JavaScriptコンパイラだったことを思い出してください。EmscriptenはWebAssemblyにコンパイルする機能を獲得しつつあります。すなわち、Rubyは次のような旅路を辿ることになります。

Rubyスクリプト → mrubyバイトコード → C → emcc(Emscriptenコンパイラのフロントエンド) → LLVM → Binaryen → WebAssembly

ちょっと無理くりすぎな感もありますが、役者は揃いつつあります!フローの中にひとつ見慣れないものがありますが、BinaryenはWebAssembly向けのコンパイラ/ツールチェインインフラストラクチャです(名前はbinaryとEmscriptenのもじり)。AlonはBinaryenのコンパイルでWebAssemblyを生成するスピーチも行っています。

ぼちぼち実際に手を動かしてやってみましょう。まず必要なのはWebAssemblyツールチェインを取ってくることです(WebAssemblyの“Getting Started”を参照)。開発に必要なものを理解したら、Emscripten SDKをダウンロード/インストールして有効にします。

$ git clone https://github.com/juj/emsdk.git
$ cd emsdk
$ ./emsdk install latest
$ ./emsdk activate latest

SDKはgit cloneで取り、他の開発ツールと同じところに置くことをおすすめします。ツールの一部はまだGitHubの個人アカウントのリポジトリに置かれています。ほとんどの開発者はMozilaで作業しているようなので、今後WebAssembly GitHub orgのようなもっとオフィシャルな場所に統合されるのかなと思ったりもします。

次はPATHと環境変数を設定するスクリプトを実行しなければなりません(source ./emsdk_env.shのように実行)。Emscripten SDKでは独自のclangとnodeが提供されるので、通常の開発で使っているバージョンと衝突する可能性があります(Rubyをソースからコンパイル/インストールしてgemのネイティブ拡張をビルドするときにそのバージョンで失敗するなど)。そこで私は、常に新しいシェルで実行するのではなく、自分の.bash_profileへのエイリアスを追加して、WebAssemblyを使うシェルでのみwasm_initでSDKをアクティベートするようにしました。

alias wasm_init="source ~/Developer/emsdk/emsdk_env.sh"

以上でEmscripten/WebAssemblyツールチェインが使えるようになります。

次はmrubyをインストールする必要があります。mirbmrbcなどの便利なコマンドラインツールにアクセスできるよう、macOSならHomebrew(brew install mruby)でインストールすることをおすすめします。Linuxならsudo apt install mruby libmruby-devを使います。

いよいよWebAssembly向けのmrubyインタプリタをビルドします。まずはGitHubのmrubyリポジトリをcloneしましょう。

git clone https://github.com/mruby/mruby.git

mruby/ディレクトリにcdします。ここからいよいよ面白くなってきます。mrubyインタプリタの実際のビルド設定は、シンプルなRubyスクリプトそのものであるbuild_config.rbファイルを定義してrakeを実行することで行います。リポジトリにあるデフォルトの設定には便利なboilerplateがいくつも詰まっていますし、ドキュメントもかなり詳しく書かれています。今回のニーズに応じて、設定ファイルの内容を以下で完全に置き換えることにします。

MRuby::Build.new do |conf|
  toolchain :gcc
  conf.gembox 'default'
end

MRuby::CrossBuild.new('emscripten') do |conf|
  toolchain :clang
  conf.gembox 'default'
  conf.cc.command = 'emcc'
  conf.cc.flags = %W(-Os)
  conf.linker.command = 'emcc'
  conf.archiver.command = 'emar'
end

新しい設定ファイルは、gccコンパイラツールチェイン(自分のシステムで同等に近いものなら何でも構いません)を用いてホストシステム向けのネイティブコンパイルを作成するようmrubyのビルドシステムに指示します。macOSの場合はgcc --versionで確認できます。

Apple LLVM version 9.1.0 (clang-902.0.39.1)
Target: x86_64-apple-darwin17.5.0

これは今使っている64-bit x86ホストシステム向けのバイナリをビルドします。WebAssemblyをターゲットとしてクロスコンパイルしたいので、ここで上のCrossBuildブロックにご登場願うことになります。このブロックではclangのツールチェインを選択してemcc(Emscripten Compiler Frontend)とemar(Emscripten LLVM Archiver)を使うよう指定します。-Osコンパイラフラグは、コードサイズ削減などほとんどの最適化を有効にするオプションです。クロスコンパイルはかなり強力で、私が別プロジェクトで作っているmrubyのiOS/tvOSフレームワークでは、静的ライブラリやヘッダーがすべて単一パッケージに同梱されています。

rakeでmrubyのWebAssembly向け静的ライブラリをコンパイルする準備が整いました。build/ディレクトリが作成され、その下にhost/ディレクトリやemscripten/ディレクトリも作成されます。欲しい静的ライブラリはemscripten/lib/libmruby.aにあります。

cd ..mruby/ディレクトリを抜けます。今度はWebAssemblyをコンパイルする以下のシンプルなスクリプトを書いてhello_ruby.rbに保存します。

puts "Hello Ruby!"

コンパイラを起動して、このスクリプトからmrubyのバイトコードを生成します。

mrbc -Bhello_ruby hello_ruby.rb

ここでちょっと解説します。mrbcはmrubyコンパイラであり、-Bはバイトコードを生成してhello_rubyという名前のCの配列に置くよう指示します。最後のhello_ruby.rbは、コンパイルしたいファイルです。生成された新しいCファイルはhello_ruby.cという名前で、バイトコードを表す16進整数の配列を含んでいます。

hello_ruby[] = {
0x45,0x54,0x49,0x52,0x30,0x30,0x30,0x34,0xcd,0x4c,0x00,0x00,0x00,0x65,0x4d,0x41,
0x54,0x5a,0x30,0x30,0x30,0x30,0x49,0x52,0x45,0x50,0x00,0x00,0x00,0x47,0x30,0x30,
0x30,0x30,0x00,0x00,0x00,0x3f,0x00,0x01,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x04,
0x06,0x00,0x80,0x00,0x3d,0x00,0x00,0x01,0xa0,0x00,0x80,0x00,0x4a,0x00,0x00,0x00,
0x00,0x00,0x00,0x01,0x00,0x00,0x0b,0x48,0x65,0x6c,0x6c,0x6f,0x20,0x52,0x75,0x62,
0x79,0x21,0x00,0x00,0x00,0x01,0x00,0x04,0x70,0x75,0x74,0x73,0x00,0x45,0x4e,0x44,
0x00,0x00,0x00,0x00,0x08,
};

このままでは単なる静的データでしかありません。これを読み込んで実行するにはもうひと頑張りが必要です。バイトコードを含むhello_ruby.cファイルに以下のコードを追加しましょう。

#include <mruby.h>
#include <mruby/irep.h>

int main() {
  mrb_state *mrb = mrb_open();
  mrb_load_irep(mrb, hello_ruby);
  mrb_close(mrb);
  return 0;
}

このスニペットではいくつかの作業を行っています。まずコアmrubyと中間表現(バイトコード)ヘッダーをインクルードしています。main()関数ではmrb_open()でmrubyコンテキストを開き、mrb_load_irepでバイトコードを読み込んで実行し、最後にコンテキストを閉じます。シンプルですね!

いよいよhello_ruby.cファイルをEmscriptenでWebAssemblyにコンパイルする準備が整いました。次のようにemccコマンドを実行します。

emcc -s WASM=1 -Os -I mruby/include hello_ruby.c mruby/build/emscripten/lib/libmruby.a -o hello_ruby.js --closure 1

個別に説明します。

  • emcc: Emscripten Compiler Frontend(LLVMのフロントエンド)。
  • -s WASM=1: WebAssemblyをオンにする。
  • -Os: スペース削減などの最適化を行う(オプション)。
  • -I mruby/include: 必要なヘッダーを含むmruby includeディレクトリを探索する。
  • mruby/build/emscripten/lib/libmruby.a: 先ほどビルドしたmruby静的ライブラリへのパス
  • -o hello_ruby.js: 出力ファイル名。JavaScriptファイルの生成を指示するこの拡張子が重要です(後述)。
  • --closure 1: Google Closure Compilerを実行して、Emscriptenの生成したJavaScriptを最小化する。必須ではないがこれもスペース削減に有効な最適化。

hello_ruby.wasmhello_ruby.jsという2つのファイルが生成されます。完全に最適化および最小化され、全部で492 KBにしかなりません。「ここでJavaScriptを生成している理由って何?」と疑問に思うかもしれません。私もこれを最終的に置き換えるんだと思っていたのですが、それは時期尚早です。このちっぽけなJavaScriptが実はなかなか役に立つことがわかります。まず、.wasmファイルを読み込むときに<script>タグを単純に使うことはできません。そうではなく、生成されたJavaScriptコードでFetch APIを用いてwasmファイルを取ってきます(実際には、将来<script type="module">でWebAssemblyモジュールをES6モジュールのように読み込めるようにする計画があります)。生成されたJavaScriptは.wasmバイトコードをWebAssembly.Moduleにコンパイルしてインスタンス化します。関数のimportやexportも設定され、メモリが設定されてアロケーションされ、コンソール出力に使うTTYなどの他の共通サービスはJavaScriptでエミュレーションされます。やってみたい方は、先のemccコマンドに--closure 1を付けずに実行し、生成された誉れ高きJavaScriptコードを調べてみましょう。“Understanding the JS API”という記事が多少なりとも参考になるでしょう。Alonは単独のWebAssemblyをビルドするガイドも書いています。

これで先ほどのささやかなJavaScriptが必要な理由が理解できましたので、これをページに読み込むシンプルなHTMLテンプレートを書いてみましょう。このテンプレートで、私たちの苦心の成果であるhello_ruby.wasmバイナリファイルをフェッチして実行します。

<!DOCTYPE html>
<html>
  <head>
    <title>Ruby on WebAssembly</title>
  </head>
  <body></body>
  <script src="hello_ruby.js"></script>
</html>

このファイルをhello_ruby.htmlという名前で保存します。このHTMLファイルをダブルクリックしただけでは期待どおりに動きません。cross-originの問題によってFetch APIリクエストがブロックされるからです。代わりにこのHTMLファイルをWebサーバーで公開しなければなりません。WebサーバーはRubyで簡単に実行できます。

ruby -run -ehttpd . -p8000

これでシンプルなWebサーバーが起動し、8000番ポートでカレントディレクトリの静的ファイルを公開します。http://localhost:8000/hello_ruby.htmlでHTMLファイルを表示し、ブラウザのコンソールを開けば、めでたく「Hello Ruby!」が表示されます🎉。

(いくつか質問をいただいたのでお答えします: 私はAtomエディタをCity Lights themeテーマで使っています😍)

そこそこ手のかかる作業なので、みなさんがもう少し簡単にこの実験を行える方法を編み出しました。

wasm Ruby gem

本記事のビルドプロセス全体を1個のgemに収めました。以下を実行してインストールします。

gem install wasm

このgemを使うには、まずWebAssembly toolchainをセットアップして、emccコマンドとemarコマンドを使える状態にしておかなければなりません(訳注: 上述の環境変数も設定する必要があります)。これで、ruby-wasmユーティリティでRubyスクリプトをWebAssemblyバイナリにコンパイルして上述のJavaScriptファイルやHTMLファイルを簡単に生成できるようになります。あとはruby-wasm serveでHTMLページを公開すればおしまいです。詳しくはGitHubのblacktm/ruby-wasmリポジトリのgemをご覧ください。

将来像

皆さんも私と同様、今後の行方が気になっていることでしょう。WebでRubyが走ることの意義とは一体何なのか、実際にはどんなことができるようになるのか、そうした可能性の追求は自己満足にとどまってしまうのか。その辺は私にも読みきれませんが、ひとつ言えるのは、現在の可能性の多くがWebAssemblyを強化するものであるということです。WebAssemblyの強みを雑に3つのカテゴリに分けると、パフォーマンスと効率性、ネイティブ世界とのやりとり、そして移植性になるかと思います。

パフォーマンスや効率に関しては、正しいコンパイル手順が使えるようになったことで分析や最適化が可能になっています。LLVMを用いることで、さまざまな最適化機能が既に使えるようになっていますし、今後の最適化機能の登場も期待できます。WebAssemblyは既に高速かつ高効率ですが、WebAssemblyのVMやランタイムコンポーネントも長年改良を重ねてきています。これは、サイズの小さい高速かつ高効率なRubyインタプリタがブラウザで使えるということであり、私たちが選ぶアプリがWebで使えるようになるというメリットを得られるということです。

ネイティブ世界とのやりとりについても素晴らしい可能性が秘められています。つまり、元々ネイティブコードにコンパイルするために書かれたソフトウェアにインターフェイスを持たせられるということです。それらの言語で書かれた既存のライブラリやツールは膨大な数にのぼり、WebAssemblyを用いればそれらをWebに持ち込めるようになります。そうしたライブラリが既にブラウザをターゲットとしているよい例のひとつが、Simple DirectMedia Layer(SDL)です。SDLは、グラフィックス/音声/コントローラ入力への低レベルアクセスを提供する、クロスプラットフォームのゲーム開発ライブラリです。SDLはブラウザAPIをサポートしただけでなく、今では従来のネイティブプラットフォーム向けに書かれたゲームを移植することが可能になりました。既にEpic Games’ Zen Gardenなどのクールなデモがいくつか公開されています。私たちRubyistにとっての可能性は、最もよい結果を得られる低レベルコンポーネントを引き続き使えることと、それらをRubyでより高度かつより概念的なレベルでオーケストレーションできるということです。たとえば、私が今関わっているRuby 2Dというプロジェクトがまさにそのよい例です。 これは、私がCで書いた小さなグラフィックエンジンであるSimple 2Dの上に構築したものですが、今やSDL上で構築されています。私の次のチャレンジは、Ruby 2DをWebAssemblyで動かすことです。Ruby 2Dのアーキテクチャに興味がありましたら、RubyConf 2017の私のトークをご覧ください。

最後は移植性、つまりRubyアプリやRubyインタプリタをどこにでも持ち込める機能についてです。mrubyは移植を完全に可能にするためのマジックに満ち満ちています。mrubyは、clangやLLVM、そしてあらゆるCコンパイラツールチェインをコンパイルターゲットにできるので、Rubyアプリはあらゆるプラットフォーム向けにコンパイル可能です。mrubyはこれまで小規模な組み込みコンピュータ(つまりIoTです: 私としてはAdafruitのCircuit Playgroundが早く使えるようになる日を心待ちにしていますし、Arduboyも面白そうです)に注力してきましたが、将来はずっと広がっていくでしょう。Rubyアプリの.wasmバイナリは信じられないほど移植性が高く、サーバーサイドでもクライアントサイドでも動かせます。たとえばAWS Lambdaは未だにRubyをサポートしていません(残念!)が、代わりにRubyのWebAssemblyバイナリをデプロイすればよいのです。RubyのWebAssemblyバイナリは移植性が高いだけでなく、MRI/CRubyで動くRubyスクリプトより安全かつ高速です。

行く手には興味の尽きない膨大な可能性が広がっていることは間違いありません。ただし、Ruby(や他の言語)がJavaScriptに取って代わることはないだろうと睨んでいます。既にWebブラウザには、非常に高い能力と何十年にもわたって最適化されてきたVMを持つプログラミング言語があるのです。特にES6がもたらす現代的な特典をもれなく利用できますので、JavaScriptで書くことは今や悪いことでも何でもありません。RubyをあえてWebに参入させたいのであれば、Opalという実に手堅いオプションを用いれば、RubyのJavaScriptへの統合とやりとりを信じられないほど簡単に行えます。

最後に、「ネイティブアプリ」と「Webアプリ」の定義の境界線は今後もあいまいになるだろうとだけ申し上げておきます。Progressive Web Apps(PWA)などのアプローチは、ネイティブ/Webを問わず最良の結果を開発者にもたらし、エンドユーザーが楽しむWebエクスペリエンスをさらに向上させ、Webの技術をデスクトップアプリとモバイルアプリのどちらにももたらします。「本当にそんなことが可能なのか?」という疑問はじきに無意味になるだろうと想像しています(ほぼほぼ「イエス」なのですから)。むしろ「それは本当によいものなのか、そしてそれをどう確かめたらよいのか?」という懸念点を解消する方が、ずっと困難で時間もかかるでしょう。最終的にどうなるのかは誰にもわかりませんが、この旅路は私たちを惹きつけて止まないことは確かです。

将来のWeb開発をよりよいものにしたいと考える人々には「今こそコンパイラの仕組みを学ぶべき」とアドバイスしておく —  Tom Dale

お読みいただきありがとうございます!

本記事でご紹介した、WebAssemblyでRubyを動かす方法が皆さまにとってお役に立つ情報となり、wasm gemでどしどし遊ぶようになることを願っています。ご意見/ご感想がございましたら、本記事のツイートに返信いただけます。このgemで何か面白いアイデアを思いついた方や、そうしたアイデアを知りたい方は、GitHubのissueを自由にオープンいただけます。いつものようにメールでお問い合わせいただくこともできます。

👋

関連記事

[インタビュー] Aaron Patterson(前編): GitHubとRails、日本語学習、バーベキュー(翻訳)

WebサイトをPWA(Progressive Web App)に変える簡単な手順(翻訳)

ライブコーディング入門:Sonic Pi篇パート1


Railsは2018年も現役か?: 前編(翻訳)

$
0
0
  • 次記事: Railsは2018年も現役か?: 中編(翻訳)

概要

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

記事は3分割しました。画像は元記事からの引用です。

Railsは2018年も現役か?(翻訳)

Rubyから別の何かに乗り換えることを検討中のWeb開発者から、数日前にこんなメールを受け取りました。

まったく新規から始めるとしたら、バックエンドにRubyかElixirかJSのどれを選ぶ?

このメールで他に重要と思われる部分は、現在の彼はもっぱら自分のサイドプロジェクトに関心を寄せていることと、主な懸念点は開発速度と開発のしやすさであるということです。

メールに返信を書き始めたのですが随分と長くなってしまったので、この際記事にすることにしました。

ご興味をお持ちいただいた方に趣旨を簡単にまとめます。

本記事では最初に、Web開発の哲学や仕事と人生についてざっと眺め、それらの意義をよりよく説明できる技術的側面に光を当てます。

忙しい人向けのまとめ

長すぎて読んでいられないという方向けに記事の冒頭に結論を置きました。ここから先は読まなくてもOKです。

これまでを振り返って考えた結果、私なら2018年の今でもRuby on Railsを主要な技術として選択するでしょう。

私はこれまで自分とよく似たタイプの人々にたくさん出会ってきましたが、Railsの技術はそうした人々(主にスタートアップ)を魅了し続けているという点で、私はRailsというプロジェクトが好きです。私は自分の好みとしてRailsを選びますが、皆さんはまた違うことでしょう。

Railsは時代遅れではありませんし、Rubyも死にかけてなどいません。むしろこれまでにないほどよくなっていますし、この技術のマーケットも実に好調です(💰💰💰)。

反論がおありの方は記事に全部目を通していただいてからお願いします。くれぐれもよろしく。

私は自分の主要なツール(つまりRails)に注力すると同時に、素のRubyやElixir、PhoenixやJavaScriptについても学習を継続するつもりです(1日20分の学習時間ぐらい誰だって確保できますよね?)。

そして上に挙げていないプログラミング言語の学習には時間を使いすぎないようにするつもりです。もちろん自分のツールボックスのツールを増やしておく必要があるからですが、かといってツールボックスにツールをぎっしり押し込めてもスキルが伴わなければろくに使いこなせずに終わってしまいます。それよりも低レイヤの技術、つまりデータベース、キャッシュ、コーディングの実践、DevOps、チーム運営、テスティングを学びましょう。

開発経験の長い人なら、新しい言語を学ぶというのもありです。

「ただのツールじゃないか」

プログラミング言語も、ライブラリも、フレームワークも、データベースも、コーディング規則も、所詮ツールに過ぎません。

壁に釘を打つときはハンマーが欲しくなりますし、レンガ塀を解体するときにはもっと大きなハンマーが欲しくなるものです。しかし自動車の修理にハンマーを使うのは普通おかしいでしょう。

画像:「Web開発者ってのは職種じゃないんだ、生き方なんだよ!」

職歴を重ねる中で、さまざまな技術や製品開発アプローチを試したり学んだりということから逃れる道はありません。「Web開発者ってのは職種じゃないんだ、生き方なんだよ!」。自己鍛錬のサイクルに終わりはないのです。

「生き方」について: ここで言う生き方とは、「仕事と生活のバランス」のことではなく、「仕事と生活のマインドセット」の話です。開発者は言ってみれば総合格闘技の選手のようなもので、異種格闘技戦の習得と実戦は生涯に渡って続きます。

ですから、プログラミング言語だけが重要なのではなく、プログラミング言語と組み合わせられる技術(データベース、クラウドソリューション、キャッシュ、ライブラリ)も重要であることに気づく必要があります。もちろんアーキテクチャ(モノリスvsマイクロサービス)や実践的チーム編成(アジャイル、スクラム、カンバン、XPgithub-flowなどなど)、コーディングの実践やコーディングスタイル(SOLIDDCIOODDなどなど)。プログラミング言語は、それらをつなぎ合わせるニカワのようなものでしかありません。

そこで最速のプログラミング言語を使ってもよいのですが、たとえばMySQLデータベースのクエリを効率よく書く方法を知らなければ、アプリは耐え難いほど遅くなってしまいます。

開発者にとっての幸せとは

プログラミング言語を使う仕事は1日に8〜10時間(下手をすると16時間)にのぼります。「開発者としての幸せ」の重要性を見過ごしてはなりません

自分が使っているツールや、自分が携わっているプロジェクトで幸せになれないのであれば、それはプログラミング言語のせいではなく、自分のせいなのです。

私は今の仕事を愛しています。それというのも、正しい技術セットを正しいチームと一緒に使える、正しい価値観に基づいた正しいプロジェクトで働ける仕事を選んできたからです。

新米開発者は、経験したくもないような業務に耐えなければならなくなることがよくあるようですが、正直に申し上げると、今この2018年の世界で、一人前のシニアWeb開発者が業務で悲惨な目に遭う理由が私にはよくわかりません。もちろんストレスや不満だらけの日々も間違いなくありますが、それもしばらくの間だけです。しかしそれを超えて悲惨なのであれば、プロジェクトや業務を変えるか、違う技術に乗り換えるか、働き方を変えましょう(在宅勤務にシフトするとか)。どれも無理ということであれば、おそらくWeb開発者としてまだ一人前ではないので、もっと精進して腕を磨きましょう(数か月後にこのパラグラフをもう一度読み返してみましょう)。

Web開発10年というのは経験が10年?、それとも毎年同じことを10回やってるってこと?

勝ち組などいない

「悪い」プログラミング言語というものはありません。あるとすれば、プロジェクトでのプログラミング言語の選択が悪かったのです。

ソフトウェア開発者がプログラミング言語をたとえば「Ruby遅すぎ」などとdisるとき、言語の長所は決してスピードではないという点を見落としています。時間をかけてそうした批判の背景を調べてみれば、たとえばろくにロードバランシングしていないサーバーで数千リクエストをさばこうとしているとか、キャッシュをまったく理解していないといったことに気づくでしょう。

開発者は、newrelicなどのツールでアプリの総合的なパフォーマンスを最適化することに注力する必要があります。その言語が扱えるループサイクル数がいくつかなどという点ではありません。今構築しているのは、現実の人間が使う現実のアプリなのですから。

開発者たちが、やれJavaは複雑すぎるだの現実のアプリには向いてないだのと不満を垂れるのをよく目にします。そうした言語は、大規模なチームによって開発され、何十年にも渡って運用されるエンタープライズ向けに設計されています。銀行であれば、即座にRubyではなくJavaを選ぶでしょう。その主な理由は、Javaが機能を廃止することがほぼないからです(どんなにクソなアイデアであっても、一度導入されれば半永久的にサポートされます)。Rubyはそうしたエンタープライズの巨人たちから受ける圧力が少ない分、大胆な変更を頻繁に取り入れる余地が大きく、コードの互換性と引き換えに優れた機能を導入できます。

Googleのように数十億ユーザーを扱うプロジェクトであれば、Rubyを使おうとは思わないでしょう。そしてこれほどの規模になるとモノリスアプリでさばくのはほぼ不可能なので、ほとんどの場合マイクロサービスアーキテクチャの形でさばいて、個別のサーバーで動く小さなアプリの中に言語の制約を隠蔽するでしょう。

詳しくは別記事『モノリスvsマイクロサービス』をどうぞ。

現在の言語がシナリオにそぐわないというだけの理由で、一部の大企業が言語を切り替えることはよくあります。Twitterが何年か後にRubyから乗り換えたのも、TwitterがSNS界の巨人となったからです。GitHubもアプリをリファクタリングしていくつかの技術に分割する必要に迫られました。これは最初の言語の選択が間違っていたのではなく、企業がそのときの姿に合わせて、自らの成長に役立つ最適な言語を選んだまでのことです。

こうした切り替えはプログラミング言語だけではなく、データベース(SQL vs NoSQL)やストレージソリューション(DropboxがAWS S3から乗り換えた)などあちこちで普通に見られます。何か事あらば騒ぎ立てる傾向はプログラミング言語関係者独特のものです。

画像: 「一部の会社が使わなくなったくらいでプログラミング言語が死ぬわけねーだろ」

言語の乗り換えは、普通によくあることです。

並外れた成功を収めるとモノリスアプリをマイクロサービスに分割するというパターンもよく見かけます。

あなたがまだ数千ドルしかファンディングを調達できていないスタートアップ関係者で、アプリをスクラッチから開発して成功させたければ、JavaやElixir Phoenixのような無駄に複雑な言語を選んでいては、リリースを迎えないうちにあっという間に資金は底をついてしまうでしょう。

もちろんElixirもPhoenixも本当にいいものです。生産性も高いし。しかし正直なところ、製品開発速度においてはRailsの方がずっと上です。

Web開発経験すらないホヤホヤのスタートアップ企業が自分たちの「画期的な」プロジェクトに欲しいものといえば、ほぼ間違いなく「プロジェクトを支持してくれる数百万人のユーザー」がそのひとつでしょう。そんなユーザーがいれば成功間違いなしですから。たいていのスタートアップ企業は、そういう手前勝手な期待を当てにして没落します。サービスを立ち上げて半年経っても、ユーザー数はせいぜい数千人がいいところでしょう。大成功してトラフィックが押し寄せるときに備えるのも結構ですが、取らぬ狸の皮算用より現実を先に見ましょう。

成功への期待が大きすぎれば、それをサポートする投資側が期待する完成時の見返りも相応に膨れ上がります。同じ機能であっても、ユーザートラフィックが数百の場合、数千の場合、数十万の場合に応じて開発期間は数日であったり、数週間、数か月と変動します。

競争の激しい市場で生き残るのは、要件に応じた既存機能の改修や新機能の導入をすばやく行えるスタートアップ企業であり、そうしたフットワークがなければたちまち消えるのが定めです。顧客や投資家にしてみれば、箸にも棒にもかからないエクスペリエンスでユーザーがせいぜい数百人程度のアプリが、将来2百万ユーザーをさばける潜在能力を秘めていようが知ったことではありません。

このように、成り行きはプロジェクトによって千差万別です。今構築中のチャットアプリはRubyで作ると初期段階から苦労が絶えず、やがてその分野はElixirに支配される可能性がもしかするとあるかもしれません。そのプロジェクトのボトルネックがどこにあるのかを真剣に分析する必要があります。

私がRubyに(そして概ねRailsにも)こだわり続ける理由は単純そのものです。私が愛しているのは面白い機能を続々開発できるプロジェクトであって、ひとつの機能を何か月もかけて辛抱強く最適化することではありません。私がプロジェクトを選ぶときは、大きすぎるプロジェクトや小さすぎるプロジェクトをあえて避けています。私にとって楽しく難易度も手頃なのはいつも中くらいのプロジェクトです。

そういうわけで、皆さんも自分たちが選んだ技術に合わせて、これから数年どんなプロジェクトで働きたいかを真剣に自問自答すべきです。

コミュニティ

コミュニティの規模や特性も見逃すわけにはいきません。言語のコミュニティ規模が小さすぎると、ライブラリやバグを修正してくれるベテラン開発者が不足します。

かといってプログラミング言語のコミュニティがあまりに大きくなると、コミュニティの劣化が加速する可能性があります。ささいなことに思われるかもしれませんが、とある言語の周辺コミュニティは、小規模だった頃にはクールかつオープンマインドを保っていたのが、市場で支配的な地位を占めるようになって人気が高まるに連れて、ポリティカルコレクトネスグループとしての性格が強まりました。手短に言うと、一部の優秀かつ人当たりもよい開発者たちがSNSで他愛もないジョークをかました途端、非難轟々の嵐にさらされたのです。

どの言語なのかは言わないでおきます。書いたら最後、コミュニティの矛先がこちらに向かって爆発炎上するでしょう。それはそれで私の言説の裏付けにはなるでしょうが、そんな自殺行為は趣味ではありませんので。


  • 次記事: Railsは2018年も現役か?: 中編(翻訳)

関連記事

Ruby/Railsのプロ開発者としての5年間を振り返る(翻訳)

開発チームを苦しめるマイクロサービス(翻訳)

Railsは2018年も現役か?: 中編(翻訳)

$
0
0

言語

それでは、初めに述べた言語たちを分析することにしましょう。

Rubyはオブジェクト指向プログラミング言語として優れています。(私見では)Rubyは優れたOOPエクスペリエンスと開発者の幸せを主要な目標に置いています。しかしコードの実行速度は主要な目標ではありません。

訳注: ご存知のとおり、現在のRubyは「Ruby 3×3」の旗のもとでコードの実行速度改善の優先順位を以前よりも上げています。

Ruby on Railsは、Ruby言語上で構築されたWebフレームワークであり、生産性と開発者の幸せを主要な目標に置いています。コードの実行速度や、癒着のないOOPエクスペリエンスはRailsの主要な目標ではありません。ソケット接続はまあまあですが、優秀というほどでもありません。

Elixirは関数型プログラミング言語として優れています。(私見では)Elixirはマルチコア処理(コードの実行速度)とソケット接続のサポートを主要な目標に置きつつ、開発者の幸せや十分な生産性にも配慮しています。

ElixirにはPhoenixというWebフレームワークもあり、Phoenixについて知れば知るほど、PhoenixがElixirと同じ目標を目指してElixirと連携するライブラリの集合体であることが理解できます。

つまり対決の図式は、Elixir vs Ruby vs Ruby on Railsということになります😄

私の説明はどの言語でも似たり寄ったりに見えますが、「魔は細部に宿る」ものです。

Ruby

オブジェクト指向プログラミングの概念(SOLID原則やオブジェクトのコンポジション、DCI、オブジェクト指向解体設計(OODD)、シンプルデザイン、境界コンテキスト、DDD、BDD、TDDなどなど)の理解が深まれば深まるほど、Rubyがこうした概念と見事に調和していることに気付かされます。

私見では、Rubyは「開発者自身を記述するのに最適な言語」です。一部の開発者もおおむねこの点を支持していますが、このことにはそれ以上に重要な点があります。皆さんの職歴のほとんどはチーム作業で占められているのですから、自分や同僚がコードに込めた意図が相手に伝わることが重要です(それこそが言語というものですから)。

よくある誤解のひとつに、Railsを使わないとまともなRuby Webアプリは書けないというものがあります。実際にはHanamiSinatraなどさまざまな選択肢があります。

RubyはWeb開発に特化した言語ではなく、汎用的なプログラミング言語であることを思い出しましょう。私はこの9年間で、純粋なRuby CLIツールを何十個も個人用や職場向けに作成しています。Google SketchupのようなRubyを実行できるアプリもありますし(訳注: 現在はGoogleの所有ではありません)、Rubyは日本ではかなり人気の高い言語です。

「Rubyは死んだ」記事はいくらでも見かけますが、裏付けを示している記事はめったにありません。たいていはグローバルなデータに立脚していない、開発者の個人的な見解や経験をぶちまけた記事です。どこかの会社が別の技術に乗り換えたぐらいで言語全体が死ぬということにはなりません。

Rubyは今日も元気いっぱいに溌剌と生きています。

Rubyは決して大メジャーではありませんが、それがマイナス要因になっていません。コミュニティのところで説明したように、実際にはこの方がむしろ望ましい状態かもしれません。

Rubyはかれこれ25年も長らえています。Rubyがそう簡単に消えてなくなることはないでしょう。

本記事を多面的に捉える裏付けのひとつとして、Railsが縮小しつつある場面をこちらの記事でご覧ください。最近のコーディングブートキャンプの科目ではRailsが他のものに置き換えられつつあります。これについては後述します。

Ruby on Rails

Railsはフレームワーク(協調動作するライブラリの集合体)であると同時に、製品を短期間で構築するためのコードはいかにあるべきかという哲学でもあります。

他の言語からRails世界に飛び込んだ開発者は、多くの場合Railsの歴史や価値体系の大きな違いを見落としています。

これらを視聴してRailsの意図を正しく理解できるようになると、Railsの作者たちが2018年に提案するソリューションが10年前と変わっていないこと、つまり今も通用していることがわかるでしょう。サーバーサイドレンダリングされるHTMLやCSS、ちりばめられたJavaScriptは今もそのままです(例のRJSはともかく)。

BasecampやShopifyの収益(何しろ営利企業ですから)や、開発者が幸せそうにしている様子(ソース)は、Railsのアプローチがうまくいっている証拠です。

Railsでは、開発者の生産性を高めるためにさまざまな要素が互いに結合(癒着)しています。たとえばRailsのモデルはデータベースに直接書き込みますし、フロントエンドはデフォルトでバックエンドと結合(癒着)しています。

最近の大きな潮流といえばSPA(シングルページアプリケーション)ですが、RailsはとりたててSPAに逆らおうとはしていません。Railsの作者たちは、単にSPAを使うことを好んでいないだけです。

ですからRailsのフロントエンドでSPAを使っても別に構いません。しかし独特なものになってしまうのも確かです。Rails 5でWebpackが標準として導入されたのはつい最近の話です。ある時期までのRailsでは、(gemを使って)アセットパイプラインでSPAライブラリをレンダリングするのが一般的でした。これはRailsバックエンド開発者とフロントエンド専門の開発者の間に緊張をもたらし、JavaScriptとRailsの「ショットガンマリッジ(shotgun marriage: できちゃった婚)」は不評を買いました。

別のアプローチは、API専用のRailsアプリを生成してSPAをまったく別のVMに配置するというものですが、DevOpsと開発チームの同期を取るために文字どおり2つの異なるプロジェクトを立ち上げるという余分な手間がかかります。

ポイントは、RailsはSPAの利用を望んでいないこと、しかしSPAが必要なら3とおりの方法が使えるということです。これは、Rails初心者や「トレンディな」最新技術であるSPAなどを教えたいコーディングブートキャンプが「RailsでSPAをやっていいのかどうか」で戸惑うだろうと私は考えます。だからブートキャンプはRailsを捨ててピュアJSバックエンドを教えるようになったのです。

理由についてはJavaScriptのセクション(後編)で説明します。

Railsの人気が落ちているように見える理由はこれです。しかし人気が落ちたのではありません。ブートキャンプがRailsを教えなくなっているらしいというだけであって、スタートアップ企業は今も新規プロジェクトでRailsを選択しています。

ここで忘れてはならないことがひとつあります。何か新しいトレンドが出現したからといって、即それ一択ということにはなりません。何年か前にもNoSQLデータベースで同じことがありました。当時はMongoDBだのCassandraだのRiakだのが、MySQLPostgreSQLなどのSQLデータベースを置き換えようとする巨大なムーブメントが巻き起こりました。その結果どうなったかというと、置き換えは起こりませんでした。SQLデータベースを使いたいシナリオもあれば、NoSQLデータベースの方が適切なシナリオもある、それだけのことです。

昨今のマイクロサービスも似たような傾向を示し、いつか来た道をたどっています。マイクロサービスが2018年のアプリにおいていかに「ベストプラクティス」であるかが方々で力説されていますが、製品リリース前に資金を使い果たしてしまうことについての説明はとんと見かけません。

開発者はいずれ、Railsがモノリスアプリに適していることと、Railsのマイクロサービスは少々メモリを食い過ぎることに気づき、Sinatraのような軽めのソリューションを採用したりするでしょう。

同じことが「コーディングのベストプラクティス」「テストのベストプラクティス」「デプロイのベストなんちゃら」などにも言えます。

チームやプロジェクトは、ひとつとして同じものはありません。プロジェクトをどう構築するか、どんな技術を使いたいか、どんな計画で進めたいのかは、他ならぬあなたがじっくり腰を据えてチームと議論しなければ決まりません。プロジェクトにとって最も迷惑なのは、毎回意見がコロコロ変わる人です。

そういうわけで、たとえば「開発速度と開発の容易さ」が主要な懸案事項だとすると、私ならSPA抜きのRailsを採用するでしょう(Turbolinksは皆さんが思っている以上に強力ですよ)。特に、チーム開発でなくワンマンプロジェクトであればきっとそうするでしょう。

「SPAとJavaScriptに専念するフロントエンド開発チーム」と「APIの提供に専念するバックエンド開発チーム」の2チーム編成が目的であれば、RailsのAPI専用オプション(rails new myapp --api)を採用するでしょう。

そのAPIで膨大なトラフィックを扱う必要があるならRailsを使いますが、EventMachineなどの別のエンジンも検討するでしょう。とはいうものの、RailsのデフォルトWebサーバーであるPumaも実に強力です。Railsのキャッシュの適用方法をきちんと学べば、相当なトラフィックを扱えるようになります。

APIでメガ単位のトラフィック(数百万リクエスト級)を扱う必要があるなら、ElixirとPhoenixがずっと優れた選択肢になる可能性もあります。

とあるPodcastで知ったのですが、米国の某市が運営する犯罪レポーティングソフトウェアはかつてRailsサーバーで運用されていたのですが、平均トラフィックが大きいため6台ほどサーバーを使っていたそうです。同市がサーバーをリファクタリングしてElixirに置き換えたところ、2台のサーバーでまかなえるようになりました。平均負荷は30%だったので実際には1台のサーバーで回せるのですが、サーバーダウンに備えて2台編成にしているのだそうです。


関連記事

Ruby/Railsのプロ開発者としての5年間を振り返る(翻訳)

開発チームを苦しめるマイクロサービス(翻訳)

Railsは2018年も現役か?: 後編(翻訳)

$
0
0

ElixirとPhoenix

Elixir関数型プログラミング言語として優れており、(私見では)Elixirの目標は、開発者の幸福やまっとうな生産性にも配慮しつつ、マルチコア処理(コードの実行速度)とソケット接続をサポートすることにあります。

PhoenixはElixir言語で構築されたWebフレームワークですが、Phoenixを知れば知るほど、PhoenixとはElixir言語と同じ目標を共有しつつElixirと協調動作するライブラリの集合体であることがわかってきます。すなわち、言語とフレームワークの間に哲学上の大きな葛藤がありません。

Elixirは、きわめて強力な関数型言語であるErlang VM上に構築されています。Erlangは、フォールトトレーランスを保証するシステムを必要とする通信業界向けに設計されており、山間部の通信塔設備において99.9999999%という優秀なフォールトトレーランスで動作します。

Erlangが満たしている通信業界の厳しい要求のひとつに、ある通信塔が他の通信塔の責務を代行することで発信元からの通信が途絶しないようにすることがあります。そしてErlangのこの分散処理能力がマルチコア通信の要請に最適であることに一部の賢い人々が気づきました。つまり、多数のCPUコア(32、64、あるいはそれ以上)を持つサーバーマシンでは、ErlangがアプリをすべてのCPUコアで効果的に走らせることができます。

「マルチコアを扱えるオブジェクト指向言語なら既にたくさんあるじゃないか(JavaはもちろんRubyも含めて)」というご意見もおありかと思います。ここで皆さんに知っていただきたいのは、あらゆるオブジェクト指向言語はマルチコアではいいところがないという点です。もちろんマルチコアでも動くは動くのですが、関数型プログラミング言語ほど目覚ましい成果は得られません。
諸悪の根源は「ステート」です。CPUコア同士がミューテーションなしでステートをやりとりするのは恐ろしく困難です。オブジェクト指向とは、行った結果であるステートを関数に加えることだからです。
関数型プログラミング言語はステートを改変しないので、副作用が生じないということになります(詳しくはこちらのブログ記事をどうぞ)。

画像:「何がむかつくって、グローバル変数を普段ほいほい改変しておきながら『AIが世界を支配する』とかぬかす開発者だよ」

ElixirはErlang上で構築されているがゆえに、この恩恵も受けられるわけです。

ソケット接続も重要な点です。あらゆるWebサーバーは、通信をリクエストとレスポンスという形で取り扱うことはご存知でしょう。しかしチャットやビデオ会議などのリアルタイムアプリを構築するのであれば、「ソケット」でサーバー上の接続を維持する必要があります。

現在のRailsやJavaは、1台のサーバーで一度に数千個の接続を扱えます。

一方、ErlangとElixirなら2百万接続を扱えます(傍証: ErlangElixir)。実を言えば40コア程度のCPUとメモリ128GBのサーバー上の軽量な接続なのですが、それを差し引いてもかなり印象的です。

つまりElixirとPhoenixの凄さは本物なのです。それならもっと使われてもよさそうなものですが、上述のとおり、何事もコストを無視するわけにはいきません。PhoenixとElixirは生産性の向上に最善を尽くしてくれるのでしょうか?将来の大規模なスケーリングを約束する技術を取り入れるとなると、癒着を切り離す方法を導入する必要が生じ、その分開発者の進捗が少々遅くなります。

「モデル」からデータベースへの書き込みはそのよい例です。(スキーマを持つ)モジュールが変更セットを呼び出し、その変更セットがリポジトリを呼び出し、そしてそのリポジトリがデータベースに書き込む必要があります()。Railsでは、この3つのステップが1つにまとめられています。

ステップを3つに分ける方がコーディングにおいてずっとよい結果をもたらします。コードが分離されることでテストが容易になるからです。しかしその分、Railsで書くときよりも明らかに時間がかかります。Railsはコーディングの実践という点では見劣りしますが、Railsは生産性と実装速度を高めることに特化しているのです。

これは「設計上の選択」とは別の話です。関数型プログラミングではコードの書き方がより明示的になる分、すべてを渡す必要があるので、よくも悪くもコードは複雑になります。

高トラフィックに耐えうるアプリを構築する場合、ソケット通信に強く依存するアプリを構築する場合、納期に余裕を持てる会社で働いている場合は、ElixirとPhoenixを使いましょう。

慌てて列車に飛び乗る前に、ElixirとPhoenix関連のトークをご覧ください。これらのトークは、皆さんが迷いを振り切って意欲的に取り組み、コードのみならず哲学をも理解するのに役立ちます。次の2つをおすすめします。

JavaScript

フロントエンドWeb開発者がサブプロジェクトをフルスタックで構築するのであれば、最小限のJavaScriptの知識は不可欠です。したがって、どの道に進もうとJavaScriptはある程度ものにしておいてください。

私はバックエンド方面で数年ほどあるサブプロジェクトに従事していましたが、当時についてはろくな思い出がなく、もう戻りたいとは思いません。当時の良し悪しを云々したいのではなく、単に私の選んだ道が正しくなかったということです。バックエンド系のJavaScript開発者で幸せそうにしている人がたくさんいることもある程度承知しています。私はその一人ではなかったというだけのことでした。そういうわけで本記事ではJavaScriptについてあまり書かない予定です。

最近よかった議論としては、現代のWebアプリではバックエンドをNode.js(JavaScript)で書き、フロントエンドをReact.jsかAngular.jsかVue.js(いずれもJavaScript)で書き、バックエンド開発者とフロントエンド開発者がJSON(JavaScript Object Exchange)でやり取りし、データベースはJSONドキュメントを保存できるMongoDBにすればいい、というのがあります。

Web開発のエクスペリエンス全体が、プログラミング言語を1つだけ習得すればいいという流れになっているように感じられます。コーディングブートキャンプがJavaScriptを好むのも、その方が単純になるのですから当然でしょう。

しかし1つ前のセクションでも申し上げたとおり、プログラミング言語を1つ学べばすべてOKということは永遠にありえません。プロとして仕事をしていれば、いずれ自分のツールボックスに新しい言語というツールを付け加えなければならなくなるでしょう。

Node.jsでバックエンドを構築していれば、同期的なRuby MRIの思想といささか異なる「イベントループ」というものと取り組むことになるでしょう。私が何を言っているのかわからない方で、かつRubyを使える方であれば、Node.jsのイベントループと似た原則を用いるEventMachineで遊んでみるとよいでしょう。

JavaScriptのもうひとつのメリットは、AWS Lambdaなどの多くのFaaSプロバイダでサポートされていることです。

最後に

学ばなければならないことは、本当にいくらでもあります(私自身そうです)。何をするにしても、暇を見つけては本を読んで勉強し、多くのコーディングを実装しなければなりません。さもないと、どんな言語を選ぼうと無駄になります。

今後数年の間役に立つおすすめのトピックをリストアップします。

  • オブジェクト指向と関数型プログラミングの両方に挑戦する
  • SOLID原則、DCI、シンプルデザイン、オブジェクトのコンポジションを学ぶ
  • デザインパターンを学ぶ(Martin Fawlerなどで)
  • DDD(ドメイン駆動設計)を学ぶ
  • Bounded Contexts(コンテキスト境界)を学ぶ
  • TDDやBDDを用いる理由と、TDDやBDDをやめる理由
  • バックエンド開発者であってもフロントエンド開発に挑戦する
  • 開発のプロになる方法を学ぶ(書籍『Clean Coder』など)
  • マイクロサービスに挑戦し、それからマイクロサービスを使うのを「やめる」

TDDやSOLID原則などの技法は有用ですが、いつどんなときも正しいというものではありません(もう申し上げましたよね!)。こうした技法を盲信しないことです。これらの技法の値打ちは、それらを学んで必要に応じて適用することにあり、技法に偏りすぎて生産的なチームを損なうことではありません。チームのよい一員であることの方がよほど値打ちがあります。

最も重要なことは、空き時間に自分だけのプロジェクトを持つことです。実験的なことは業務以外のプロジェクトで行い、そこで得られた知見を仕事に反映しましょう。

「自分の時間を潰してまで何かを学ぶつもりはない、今の会社で必要なことをその場で学べばいい」という態度は、開発者として最悪です。Web開発者とは単なる仕事ではなく、生き方なのです。Web開発者としての人生を歩みましょう。

本記事に関連する議論


訳注: Martin Fowler「Refactoring 2nd Edition」がつい最近リリースされました。

関連記事

開発チームを苦しめるマイクロサービス(翻訳)

週刊Railsウォッチ(20190409-2/2後編)Ruby 2.3系サポート終了、Thoughtbotのコーディング指南書、PostgreSQLのgenerated column、Chromebrewほか

$
0
0

こんにちは、hachi8833です。自宅でKindle for Macにダウンロードされる画像がちょくちょく欠落する病が治らず困っています😢

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

今回の後編も、4/8の第9回公開つっつき会の内容を元にしています。ご参加いただいた皆さまありがとうございます!

⚓後出し臨時ニュース: Ruby 2.3系のサポートが終了(Ruby公式ニュースより)

先週の公開つっつき会で取り上げようと思っていたのが漏れていました🙇。Rubyにとって春は別れの季節ですね。

Ruby 2.3以前は今後セキュリティパッチがリリースされませんので、お早めの対応をどうぞ。

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

⚓faastRuby: RubyやCrystal向けのサーバーレスプラットフォーム


faastruby.ioより


つっつきボイス:「faastRubyは例のRuby on Jetsの対抗馬かな?と思って: とりあえずQiitaの記事も貼りました」「ぱっと見にはAWS Lambda上で動くサーバーレスなRubyという感じだけど、Faster than Lambdaとか書いてるし😆」「ホントに?」「Heroku並に簡単でLambdaより速いという謳い文句ということはLambdaじゃないサーバーレスということか」

「faastRubyでデプロイできて、ローカルHTTPサーバーもあって、と、pricingのあたりを見るとやはり別途サーバーが必要ということなのかな?」「あ、有料ありですか」「Self hostedプランもあったりするので、LambdaではないfaastRubyサーバーを立ててそこにデプロイするというもののようです☺」「Heroku的なサーバーレスサービスを狙ってるのかも🤔

「いま話に出てきたJetsは、Railsウォッチでも何度か取り上げたんですが、こちらはAWS Lambdaにデプロイするタイプですね」


rubyonjets.comより

⚓不揮発性メモリ(Publickeyより)


つっつきボイス:「そうそう、これも出てましたね😋: こういう↓のをDRAMのところにぶっ刺せる」「最初はもう少し小さめのヤツだった気がしますね」「この製品については今後どうなるかはまだわかりませんが😆: OSやハードウェアのサポートも必要になりそうですし」「マザボとの相性とかありそう😆

「こういうのを既にやっていそうなのは、やはりクラウドサービスのプロバイダでしょうね(GoogleとかFacebookとか): つかそういうところにこの製品を売りたいでしょうし😆」「最近はあまり追えてないけど、こういう高速デバイス周りはいろいろ論文が出ていそうな気がするので、追ってみると楽しいかも😋

⚓その他クラウド


つっつきボイス:「Goのバイナリ、そりゃ動くよね🤣」「Goがというより、バイナリもやっぱりLambdaで動かせるんだなと思って😆」「LinuxのELFバイナリなんかは普通に動くでしょうね☺

参考: Executable and Linkable Format - Wikipedia


⚓SQL

⚓PostgreSQLに「generated column」が追加(Postgres Weeklyより)

-- 同commitより整形
CREATE TABLE gloc1 (a int, b int);
ALTER TABLE gloc1 set (autovacuum_enabled = 'false');
CREATE FOREIGN TABLE grem1 (
  a int,
  b int GENERATED ALWAYS AS (a * 2) STORED)
  SERVER LOOPBACK OPTIONS(table_name 'gloc1');
INSERT INTO grem1 (a) VALUES (1), (2);
UPDATE grem1 SET a = 22 WHERE a = 2;
SELECT * FROM gloc1;
 a  | b  
----+----
  1 |  2
 22 | 44
(2 rows)

SELECT * FROM grem1;
 a  | b  
----+----
  1 |  2
 22 | 44
(2 rows)

つっつきボイス:「generated column?」「誰か分かる人います〜?」「言葉自体初めてかも😅」「あ〜そういうことか↓、なるほどなるほど」

参考: JSON型にindexも!MySQL 5.7のGenerated Columnsの可能性について探る - UUUM攻殻機動隊(エンジニアブログ)

簡単に言うと、トリガーで特定のカラムにデータを入れるのを簡単に定義する方法みたいなもので、カラム定義時にAS 計算式と書くことで、そのカラムの値が該当の計算結果になります。
この機能はMySQL 5.7.6から追加されました。日本語では「生成列」と呼ぶこともあるようです。
上記事より

-- 上記事より
mysql> CREATE TABLE triangle (
    ->   sidea DOUBLE,
    ->   sideb DOUBLE,
    ->   sidec DOUBLE AS (SQRT(sidea * sidea + sideb * sideb))
    -> );
Query OK, 0 rows affected (0.11 sec)

mysql> INSERT INTO triangle (sidea, sideb) VALUES(1,1),(3,4),(6,8);
Query OK, 3 rows affected (0.01 sec)
Records: 3  Duplicates: 0  Warnings: 0

「上のsidecみたいに、他のカラムから計算した結果をカラムの値にできるヤツ」「おぉ?」「言ってみれば、カラムで関数を定義できるような感じ」「標準SQLにあるんだっけか?」

後で調べると、Microsoft SQL Serverでは「computed column(計算列)」と呼ばれているようです。またWikipedia(英)によると、一般にはvirtual columnという名称らしく、標準SQLにはないものだそうです。

参考: 計算列のインデックス - SQL Server | Microsoft Docs
参考: Virtual column - Wikipedia

⚓JavaScript

⚓Visual Studio 2019がリリース(Publickeyより)


つっつきボイス:「おーVS2019!」「ところで、この中でVisual Studio使ってる人います?」「VSCodeなら☺」「あ、Visual StudioとVSCode(Visual Studio Code)って違うのか😅」「元C#erです😆」「しーしゃーぱー😆

参考: Microsoft Visual Studio - Wikipedia
参考: Visual Studio Code - Wikipedia

「最近だとJetBrains IDEにもReSharperなどのC#の開発環境もありますけど、C#だとやっぱりVisual Studioが強いのかなーなんて」

「VSはとりあえずMatzもうらやましがってるIntelliCodeが気になります😊」「IntelliCode、名前的にJetBrainsに挑戦してる感🤣」「あは🤣

参考: ReSharper: The Visual Studio Extension for .NET Developers by JetBrains
参考: Rider: The Cross-Platform .NET IDE from JetBrains
参考: IntelliJ IDEA: The Java IDE for Professional Developers by JetBrains

「IntelliCodeはAI使ってるらしいとか聞いてますが、コードレコメンドとかやってくれるみたい」「そのうちみんなでコピペコードが書ける時代になったりして😆」「😆

参考: IntelliCode を使ってみて戦慄した - Qiita

⚓ChromeコンソールAPIの新技集


つっつきボイス:「最近はてブで上がってましたね↓」「はい、それの元記事です」「ログを入れ子にする(グルーピング)↓とか、実はChromeコンソールでいろいろできるらしい」

参考: 【2019年4月版】Javascriptのconsoleがすごいことになってた。 - Qiita

// 同記事より(以下同文)
console.group();
console.log("Inside 1st group");
console.group();
console.log("Inside 2nd group");
console.groupEnd();
console.groupEnd();
console.log("Outer scope");

「コンソールログにCSSを当てられる↓ってそれって🤣」「変態や〜🤣

console.log("Example %cCSS-styled%c %clog!",
    "color: red; font-family: monoscope;",
    "", "color: green; font-size: large; font-weight: bold");

「WarningやErrorのスタイルにできる機能↓とかは実用性あるかも😋」「いつの間に実装されてたんだろ?」「結構前からあったかも☺」「お〜」

console.warn("This is a warning!");

console.error("This is an error!");

「assertもあるし」

console.assert(true, "This won't be logged!");
console.assert(false, "This will be logged!");

「TimingとかCountingとかは前からあった気がする」

console.timeLog(); // default: [time] ms

console.count(); // default: 1
console.count(); // default: 2
console.count(); // default: 3

「ついでに、この中でJavaScriptを業務でゴリゴリ書いているフロント系の人っています?」「ちょこちょこ触るぐらいかな〜☺」「お、じゃ結構モダンなJS環境だったりします?」「Reactとか使ってますが、まだjQuery案件多いですね😅」「やはり〜😆」「まだまだjQueryは生き続けると☺

「ES2015は使ったりします?」「今徐々に移行しつつある感じで☺」「よかった〜、みんな未来に向かって進んでるらしいことがわかって😆

⚓その他JS

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

⚓Kururu: GitHub向けテスト管理ツール


同サイトより


つっつきボイス:「社内Slackのtest板に投下されていたので」「CI回すとかではない?」「どちらかというとissueというかチケットの管理っぽいです」「チケット管理側とコード管理側を紐付けるみたいな感じかな🤔」「とりあえずGitHub専用みたいです」「unitテストとかじゃなくてintegrationテストなんかだとシナリオに沿ってやるから、こうやってチケット書いて対応するみたいなのは欲しいのかも🤔

開発者自身がテストを担当するレベルの小さなチームやプロジェクトを意識してます。 その上でテスト管理とチケット管理を連携しやすいツールを作ろうと考えました。あえてGitHubと連携を必須にしたのはこれが理由です。自分自身の経験から小さなチームであればGitHubのissueでチケット管理するのが一番楽でした。
そのため、Kururuではもしテストが失敗したら速やかにGitHubと連携できるように作りました。 もちろん、現状ではまだまだ機能が少ないので追加や改善をしていこうと考えてます。
同サイトより

⚓「令和の」令が2つ


つっつきボイス:「令和が早くも😆」「令(U+4EE4)と令(U+F9A8)の2つか〜、これはもうしょうがない😇

正しいと考えられる方:

下のCJK文字はおそらく使われない:

compart.comより

「ところで、令和ってどう発音するのが正しいんだろか?😆」「れいわ↑?」「れ↑いわ↓?」「(フラットに)れーわ?」「あれどれだろう😆

参考: 「令和」のアクセントは? 放送各社で対応分かれる (1/2) - ITmedia NEWS

一応私のオレオレRailsアプリにも令和の誤りを登録しました↓。


enno.jpより

⚓Chromebrew: Chrome OSで使えるHomebrew


同サイトより

$ crew install vim

つっつきボイス:「Chrome OSで使えるhomebrewみたい」「Chrome OSというかChromebookって使ってる人います?」「一応気にはなってます☺

参考: Google Chrome OS - Wikipedia

「Chrome OS、実は結構いいですよ〜❤: 今自分が授業やってる学校の先生が最近Chromebookのエントリーモデル使っているのを見てると、論文レビューとかに使う分にはまったく問題ない感じですね」「なるほど😍

参考: Chromebook - Wikipedia

「やったことはないんですが、Chromebookの背面の小さな蓋を開けてDIPスイッチを切り替えると、Chromebookのローカルに書き込めるようになるという噂を見かけたことあります」「それ、やりました✋」「お、どうでした?」「壊れました😇」「やっぱし🤣」「ぶっ壊れですよもう🤣」「ご愁傷様です🙏

今朝思い出したのですが、私事ながら自分の息子が今年入学した中学では2年生になると全面的にChromebookで授業やるそうです。来年あたりに危うく子どものChromebookのDIPスイッチをドヤ顔で動かして自滅するところでした😅

参考: ディップスイッチ - Wikipedia

⚓その他フロント


つっつきボイス:「とにかく動画を再生」(ぽちっ)「おおwこれは😆」「面白え〜」「しかも左上のところにスコアが表示されるあたり芸が細かい😋」「アイデアの勝利👍」「絵が描けなくてもできるし」「さすがげえむくりえいたあ」

⚓言語・ツール

⚓Playbook: Thoughtbot謹製の汎用プログラミングガイド


同リポジトリより

例:

  • ベストプラクティス
    • 一般
    • オブジェクト指向設計
    • Ruby
    • RubyGem
    • Rails
    • テスト
    • Bundler
    • RDB
    • PostgreSQL
    • Email
    • Web
    • JavaScript
    • HTML
    • CSS
    • Sass
    • ブラウザ
    • Objective-C
    • Bash
    • Haskell
    • (略)

つっつきボイス:「Thoughtbotが作った、コードはこう書けみたいな心得帳というか格言集というかガイドなんですが、カバー範囲がやけに広いのが面白いと思って」「そういえばThoughtbotって最近どんなgem作ってたかな?」「最近ちょっとおとなしめな印象ありましたね」

「Thoughtbotといえばfactory_bot、さすがに今も動いてる」

「あと何が有名だったかな〜」「DeviseはPlataformatecだから違うし」「んーと、Paperclip!」「それだ!」「Paperclipはもうサポート終了なので😇」「年季の入ったプロジェクトにはまだあるかも?😆

Paperclipからの移行ガイド↓です。

Rails: PaperclipからActiveStorageへの移行ガイド by thoughtbot(翻訳)

「Thoughtbotの有名なgemって他にもあった気がするけど、リポジトリにたくさんありすぎてわからない😅」「backbone-js-on-railsなんてのを見つけました↓」「お〜歴史⛩」「歴史だ」「一時期はBackbone.jsこそ本命みたいに言われてた時期もありましたね☺」「あっという間に消えた」「あんなに真っ逆さまに消えるとは思わかなったし」

⚓その他

⚓Desmos: 直感的な数式プロットサービス


desmos.comより

生成したグラフの埋め込みもできます↓。


つっつきボイス:「Desmosは以下のブログの方が愛用しているそうです」(サイトを開く)「おぉ、これは絵から数式を起こすこともできるヤツ?」「そうですね、どっち方向もやれますね」

「ミッキーマウスがドラえもんにモーフィングするとか」「フーリエ展開とか使って」「しかしスポンジボブ😆

参考: フーリエ級数 - Wikipedia

「ついでに以下も貼ってみました」「ほー、階乗の!記号の由来が印刷の都合で…ってホントか?🤣」「案外4/1だったりして🤣

「ってこの日付、4/1みたいですけど😆」「あー、ネタだったか〜!😇」「ネタ説濃厚😆」「引っかかった〜😆」「真っ赤な嘘😆」「ワンテンポ遅れで四月の魚になってしまいました😅

四月の魚: Poisson d’avril〈フランス語〉〔仏ではこの日は魚の紙切れを誰かの背中に黙って貼り付け、皆で笑い者にする。アイルランドも他人に紙を貼り付ける(Taily Day)。〕

⚓英語の「R」を発音しない流行があった


つっつきボイス:「これイギリスのBBCサイトの記事なんですが🇬🇧、予想以上にいい記事でした😍: おまいらRも発音できないのかみたいにマウント取りに行くのかと思ったら全然違ってたという」「イギリスやアメリカですら、Rの発音が時代によって品がないとされたり、Rの発音を弱めてオシャレ感を出したりしてた時期があったんだそうです」「翻訳が極上なのもポイント高いです😋: こんな訳しにくいものよく訳したなと」「RというとR言語↓しか思い浮かばないけどっ🤣」「それ🤣

参考: R言語 - Wikipedia


r-project.orgより

Fred Astaireの歌う古い英語の歌↓がとても聞き取りやすくて好きだったのですが、言われてみればいわゆるRの音がほとんど強調されてなくて単なる「アー」みたいに聞こえます。もしかするとマイクの性能が低かった時代のステージでの歌い方だったりするのかもしれませんが。

参考: フレッド・アステア - Wikipedia

⚓番外

⚓文明と衰退


つっつきボイス:「政権交代すると学問が衰退するというのはあるある」「逆もあったりしますし」

「これとは少し違うけど『権力者は暦を操りたがる』みたいな傾向ってあったりしますよね」「ありそう〜」「暦とか元号の改定を権力の証とするみたいな」「ピラミッドとかより安上がりに歴史に名前残るからかも😆

そういえばプーチンは何年か前にサマータイム周りをエイヤっと変更してましたね。

参考: 【続報】新タイムゾーン決定~10月26日から東京・モスクワ間の時差が通年6時間に~ | ロシアぶろぐ(仮)~目指せ1日1ロシアネタ~

⚓ダークマターあやうし?


つっつきボイス:「現時点ではまだ何とも言えないんですが、ここ何年かでダークマター説がじわじわ苦しくなりつつあるような感じだったので」「久々の宇宙ネタ🛰」「この辺好きなの自分だけかも😆

参考: 暗黒物質 - Wikipedia
参考: エントロピック重力 - Wikipedia


今回は以上です。

バックナンバー(2019年度第1四半期)

週刊Railsウォッチ(20190408-1/2前編)RubyKaigiの予習資料、Rails「今年ベストのプルリク」、numbered parametersの議論ほか

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

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

Ruby 公式ニュース

Publickey

publickey_banner_captured

Postgres Weekly

postgres_weekly_banner

週刊Railsウォッチ(20190416-2/2後編)最近のRDBMS市場、Flutterがデスクトップにも向かう、書籍『失敗から学ぶRDBの正しい歩き方』ほか

$
0
0

こんにちは、hachi8833です。明日博多入りいたします🍜。唯一の心残りはクラフトワークの来日がRubyKaigi 2019の会期ともろにぶつかったので泣く泣く諦めたことです。

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

お知らせ🛎

来週の週刊RailsウォッチはRubyKaigiでつっつき会が開催されないためお休みいたします🙇

⚓はみだしRuby/Rails

つっつきの後で見つけたツイートなどをいくつか貼ります。

先週開催されたRejectKaigiのスライドが他にも続々上がっています↓。




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

⚓Googleが続々サービスを発表


つっつきボイス:「クラウド方面の速報はもうPublickeyの方にお任せします: Railsウォッチのペースだと到底かなわないので😆」「😆

「DockerコンテナをGoogleのクラウドで実行できるようになったのかー」「コンテナになっていれば何でも実行できるってことですよね、しかもサーバーレスで」「そういう時代か👴」「コンテナがでかくならないようにしないと」「public betaらしいので今のうちに遊んでみようかな😋」「容量どのぐらいまで置けるのかな😎

「もうひとつのニュースもちょっとびっくりですね😳」「Active Directoryを?Googleが?マネージドで?」「Microsoft SQL ServerならまだわかるけどActive Directoryもやるとは!」「しかもLDAPじゃなくて」

参考: Active Directory – Wikipedia
参考: Lightweight Directory Access Protocol – Wikipedia

参考: 第1回 誰も教えてくれないActive DirectoryとLDAPの「本当の関係」[前編]:知られざるActive Directory技術の「舞台裏」|gihyo.jp … 技術評論社

「LDAPって今もあるのかな?😆」「最近あまり聞かないけど、まだまだあるはずですよね🤔」「終わってはいないハズ」「使おうとしたというより、既に使われちゃってることの方がありそう😆」「LDAPって込み入っててわけわからなくなりがちなので触りたくない気持ち😭」「いつだったか、Railsの認証にLDAPが絡んでたのを見たことがあります😇

参考: RubyからLDAPのデータを扱うgem net-ldap | 酒と涙とRubyとRailsと

⚓AWS Innovateオンラインカンファレンスが開催中


aws.amazon.comより


つっつきボイス:「実はこのAWS Innovate Online Conferenceは既に開催が始まっていて、通常のセッション以外にも、期間中はAWS認定の有料トレーニングコース(「ソリューションアーキテクト – アソシエイト」)を無料で受けられるそうです」「へぇ〜」「メインのセッションは4/26(火)で営業時間中ですが😆、5/7(火)までやってるのでゴールデンウイークにもやれそうですね」「修了すれば証明書を発行とな」「ちょうど春の研修のタイミングだし、やってみようかな😋

参考: AWS資格勉強を始めようと思っている方!今がチャンスですよー 〜AWS Innovate試験対策セッションのご紹介〜 | DevelopersIO

「AWS Innovate、日本の昼間の時間帯にやるんですね」「あ、以前のAWS Innovateは海外で開催でしたっけ」「2018年からオンラインカンファレンスになってるのか」

⚓補足: AWS Summit Tokyo 2019

AWS Summit Tokyo 2019(6/12〜6/14)も募集開始されました。今年はRubyKaigiと会期がぶつかっていないのがうれしいですね😋

⚓Nuclio: オープンソースのサーバーレスプラットフォーム(Serverless Statusより)


つっつきボイス:「NuclioというオープンソースのサーバーレスプラットフォームをAWS Lambdaと比較する記事です」「今日のつっつきは惜しくもmorimorihogeさんが都合で出られないので、この辺りはあんまり深い話はできないと思います🙇」「Lambdaだと関数を書くときにいくつか制約があったりするという話が以前もありましたが(ウォッチ20190225)、Nuclioならできるみたいなものもあるようです」「今ならラムダって何だろうみたいな人も多いだろうから、今Nuclioがサーバーレスに参入するならチャンスあるのかも🤔


同記事より

同記事の中から、両者の比較部分のごく一部を抜粋しました。同記事の結論ではNuclioを持ち上げていますが、セカンドオピニオンが欲しいです😅

  • Lambdaのコンカレンシーモデルではアプリ開発が楽で呼び出しベースの課金がやりやすいが、関数がIO待ちのときにCPUサイクルをidleにできず、スケールするにはコールドスタートが必要。
  • Nuclioの「関数プロセッサ」(=コンテナに似ている)は関数ワーカーを複数保持でき、各ワーカーが1度に1つのメッセージを処理できる。同じ関数プロセッサ内でのコンカレンシーをサポートし、負荷に応じて関数プロセッサのレプリカ数を自動的にスケールする。
    同記事より抜粋・再構成

「ところでNuclioはどこに置いたらいいんでしょう😆

リポジトリを見ると、以下の4つのセットアップ例がありました。ラズパイでも動かせるようにする予定のようです。

⚓モバイル

⚓Flutterがデスクトップも目指している


flutter.devより


つっつきボイス:「BPS社内のFlutter部の方がSlackに投下してたので」

以下はFlutter部からの追伸を再構成したものです。

「FlutterでデスクトップOSアプリ、は実は以前より粛々と開発が続いていました↓」

「今は自分のアプリを試そうと思ったら例えば『exampleディレクトリのサンプルを自分のコードに入れ替える』ような形でやれます↓」

「冒頭のissueは、これをflutter開発のメインのコマンドであるflutterコマンド経由で
プロジェクトを作成、ビルド、実行できるように整備していく事に本腰入れはじめたissueのようです」

「なお、追加プロジェクト作成のissue達はこちらです↓」

参考: Support flutter create for macOS targets · Issue #30703 · flutter/flutter
参考: Support flutter create for Windows targets · Issue #30704 · flutter/flutter
参考: Support flutter create for Linux targets · Issue #30705 · flutter/flutter

⚓SQL

⚓書籍『失敗から学ぶRDBの正しい歩き方』


つっつきボイス:「この間のPHPerKaigiを見に行ったメンバーが以下のスライドでこの本を知って、今日のWebチーム内発表で触れていたのでチェックしました」「そうそう☺

第1章 データベースの迷宮
第2章 失われた事実
第3章 やり過ぎたJOIN
第4章 効かないINDEX
第5章 フラグの闇
第6章 ソートの依存
第7章 隠された状態
第8章 JSONの甘い罠
第9章 強過ぎる制約
第10章 転んだ後のバックアップ
第11章 見られないエラーログ
第12章 監視されないデータベース
第13章 知らないロック
第14章 ロックの功罪
第15章 簡単過ぎる不整合
第16章 キャッシュ中毒
第17章 複雑なクエリ
第18章 ノーチェンジ・コンフィグ
第19章 塩漬けのバージョン
第20章 フレームワーク依存症
amazon.co.jpより

「タイトルひとつひとつがとっても読んでみたい欲をそそります😋」「『効かないINDEX』🤣」「キャッチコピーとしてもいい感じ🤣」「『転んだ後のバックアップ』このままかるたになりそう🎴」「転ばないとバックアップしないとかあるある〜🤣」「『監視されないデータベース』に『知らないロック』とか、噛めば噛むほど味が出る感」「キャッシュ、中毒になるんですよこれが😅」「『ノーチェンジ・コンフィグ』って、素のままの設定で本番にぶちこんじゃうとか?」「『塩漬けのバージョン』もいろいろ思い出すし」

「この本、翻訳とかじゃなくて日本語で書かれてるし」「こういうのは翻訳だとガタピシしてしまいがちなので、これだけキマったタイトルにするのは至難の業です😭」「翻訳臭がないのはうれしいですね😋」「内容期待できそう😍

参考: そーだいなるらくがき帳 — 曽根さんのブログ

⚓PostgreSQLのcount(*)を高速化(Postgres Weeklyより)

-- 同記事より
SELECT count(*) FROM large_table;

つっつきボイス:「ぽすぐれのcount(*)を高速化する話だそうです」「うろ覚えですが、Oracleだとこういう書き方するとめちゃ遅くなるから何らかのカラム名を書けって言われたことあったな〜: 他のRDBMSではどうなのか知りませんが😆

「まずPostgreSQLはvisibility mapを使ってindex onlyスキャンを高速に行えるから、VACUUMしようみたいな話」「それから集約テーブル(aggregate table)」「集約テーブルって何でしたっけ😅

参考: 第3回 テーブル設計のグレーゾーン~毒と薬は紙一重 (2)列持ちテーブル :SQLアタマアカデミー|gihyo.jp … 技術評論社

このように出力の形に合わせたテーブルをフロントに持つ方法は,古くから利用されています。データを集計した結果を保持する集約テーブル(サマリーテーブル)も,このタイプのひとつに位置付けられるでしょう。
同記事より

count(*)は結局どこかのタイミングで数えるしかないんですが、WHEREみたいな条件がないとどこにインデックスをかけたらいいのか判定できなくてフルスキャンされたりすることがあるし」「あ〜」

「元記事には『count(*)がそもそも要るのか?』という話もありますね」「count(*)的なものが欲しいことはたくさんあるんですが、count(*)でなくても取れればそれでいいんだし😋」「かといって*使わないと、SELECTしてないidを数えるかどうかみたいなときにidが主キーじゃないととても面倒とか、とにかくいろいろ大変になるわけです」「ふむ〜」「なのでとりあえずcount(*)にしとけば文法的にも正しいし、という感じでやることもあるといえばある」「あとはRDBMSの実装次第かな、さすがに毎回フルスキャンはしないと思いたいけどっ😆

「あと元記事に『MySQLのMyISAMには”magical row count”があるけどPostgreSQLにはない』みたいな話もありますね」「あ〜、どのRDBMSだったか、行に勝手にidを振るやつがあるけどMySQLでしたか」「そこを見れば一発でカウント取れるという感じですね」「思いっきりRDBMS依存のコードになりますが😆

参考: MySQLでテーブルの行数を数える | b.l0g.jp

⚓最近のRDBMS市場(DB Weeklyより)


同PDF p32より


つっつきボイス:「最近のRDBMS市場のサーベイPDFです: 割と長いので目ぼしいグラフをちょっと覗いてみます👀」「Oracleは何位かな〜☺


同PDF p43より

「Oracleは安定のトップ」「MySQLって2位なんだ!」「3位がMicrosoft SQL Server」「ぽすぐれが意外にも4位でMongoDBとどっこいどっこいとは😳」「PostgreSQLの今年3月のスコアはMySQLの1/3ぐらい…」「ぽすぐれもっと評価されてるかと思ってた」

「自分も今の会社に来て初めてPostgreSQL触っていい子😍だってわかったけど、それがなかったらMySQL選んでたかも」「Microsoft Accessが7位につけてるのも何というか😆」「FileMakerもいらっしゃるし」「ここに入れていいんだろうか😆」「SQLite(10位)がAccessより下だし😳」「SQLiteもっと強いかと思ってた😅」「自分も〜」「クラウド系のDynamoDBもまだ下の方ですね」「きりがないのでとりあえず次へ👉

⚓その他SQL


⚓JavaScript

⚓WebAssemblyを20倍速にした話(JavaScript Weeklyより)


同記事より


つっつきボイス:「JSアプリをWebAssemblyにしてさらにチューニングして20倍速くしたそうです」「WebAssemblyがまだよくわかってないけどっ😆」「minifyしたとかじゃなくて?😆」「WebAssembly(wasm)は、すごく大雑把に言うとブラウザで実行できるバイナリですね」


webassembly.orgより

参考: WebAssembly - Wikipedia

「バイナリにしてどうするんだろ」「えっと、ちっちゃくなって速くなる: 自分が試しに以下の記事でmrubyで動かしてみたときはJavaScriptからwasmを起動してました」「Cのライブラリを呼べるとかそういうのとはまた違うのかな」「CやC++の他にRustやGoでもwasmバイナリを吐けるようになってますね: でもまだ新しいから最適化とかはこれからという感じで」「へぇ〜」

mrubyをWebAssemblyで動かす(翻訳)

「ブラウザ上で動くVMみたいなもの?」「wasmはスタックマシンだそうです」

# 同記事より
# Compile to WebAssembly
$ emcc seqtk.c \
    -o seqtk.js \
    -O2 \
    -lm \
    -s USE_ZLIB=1 \
    -s FORCE_FILESYSTEM=1

「記事の方はいろいろ工夫して、最終的にJSの21倍に高速化したそうです↓」「『いつもこううまくいくとは限らない』ってありますが😆」「そりゃそうだ😆


同記事より

⚓「shotgun surgery」とReactコンポーネント(React Statusより)


つっつきボイス:「記事は割と長いのでタイトルだけ見ると、shotgun surgeryで『できちゃった婚』を指すshotgun marriageを思い出しちゃいます😆: 『うちの娘と結婚するか今ここで死ぬか』をショットガンで迫るという」

参考: shotgun marriageの意味・使い方・読み方 | Weblio英和辞書

【由来】 妊娠させられた娘の父親が相手の男に shotgun をつきつけて結婚を強制したことから》
weblio.jpより

「と思ったら、このshotgun surgeryはどうやらアンチパターンの名前みたい😅: 1箇所変えると他のあちこちも変えないといけなくなることだそうです」「やっぱり銀の弾丸はないっ🔫

参考: Shotgun surgery - Wikipedia
参考: Shotgun Surgery


refactoring.guruより

「できちゃった婚になぞらえてshotgun surgeryを深読みすると、『全部作り直すか死ぬか2つに1つだ』みたいな意味にもなるかな、なんて🤣」「仕事でも実際に迫られるヤツだ🤣」「作り直すには工数が足りないんだけど、このままメンテするのもつらいような状況🤣」「作り直す方が早いことも多いのに😢」「よく起きる問題ですよね☺

⚓is-online: オンラインかどうかを確かめるライブラリ(JavaScript Weeklyより)

// 同リポジトリより
const isOnline = require('is-online');

(async () => {
    console.log(await isOnline());
    //=> true
})();

READMEを見ると、ブラウザ環境ならNavigator.onLineで確認できるけど、それができないブラウザ以外のJS環境で使うそうです。

参考: Navigator.onLine - Web APIs | MDN

⚓その他JS


同リポジトリより


つっつきボイス:「ギターのタブ譜を生成するJSなんですが、タブ譜(tablature)ってご存知です?」「うんにゃ😆」「ギターを始めて間もない人が割とよく使うギター専用の譜面の記法で、ギターのどこを押さえるかをこんなふうにビジュアル表示します: 私は楽器が違うんでタブ譜に用はないんですが🤣」「🤣

参考: タブラチュア - Wikipedia

「ギターの人で、普通の五線譜は読めないけどタブ譜なら読める、という人を割と見かけることがあって、ちょっと不思議だなと思ってました」「押さえる位置が物理的に示されているからわかりやすいのかも?」「ギター、音楽の授業でちょっとやったけど指痛くてやんなった😆」「最初痛いっすよねホント😆

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

⚓GSMArena.com: さまざまなスマホのスペックを同じフォーマットで見られるサイト

BPSのテスティングチームの人がこのサイトに喜びの声を上げていました🥰


つっつきボイス:「サイトを開いて、スマホのメーカーと機種をクリックすると、そのスマホのスペックが表示されるんですが、その表示が以下のようにどのメーカー/機種でも同じフォーマットに揃えられています↓」「そこ重要!」「これは欲しいヤツ❤」「よくぞ作ったと思います👍


gsmarena.comより

「いいっすね〜(しみじみ)、ノートパソコンでもこういうサイトが欲しいです💻」「ノートPCはさらに大変そうだけど😆」「この間ノートパソコンの液晶のサイズを調べたんですけど、メーカーごとにいろんな表記方法があってつらいことつらいこと😭」「公式サイトにもちゃんと載ってなかったりしますし😆

⚓その他フロント


つっつきボイス:「Edgeのpreview buildがChromiumエンジンを採用するという記事です」「それをEdgeと呼んでいいものかどうか😆」「エンジンがChromiumならコスメティックな部分は各社好きにやってくれていい気がします☺」「Edgeほとんど使ってませんけど😆

⚓言語・ツール

⚓謎のV言語

// 同サイトより
fn main() {
    types := ['game', 'web', 'tools', 'GUI']
    for typ in types {
        println('Hello, $typ developers!')
    }
}

つっつきボイス:「この間社内で教えてもらったV言語という新言語なんですが、登場して数日で★が2000超えてました」「バイナリが小さいらしくて、一説にはRustやGoのライバルと言われているみたいです」「ほ〜」「ただ、私の探し方が悪いのか、まだコンパイラが見当たらないんですよね😅」「リポジトリにあるのはV言語のサンプルのようですね☺」「サイトにはplaygroundがあって動くし、soon availableとか書いてあるのでそのうち出るのかもしれませんが🤔」「また一文字言語が現れたか😆」「CとかRみたいな😆

⚓Goはどうしてデカい?(Golang Weeklyより)


同記事より

  • リポジトリ: jondot/goweight — Goバイナリサイズの分析ツール


同リポジトリより


つっつきボイス:「Goのバイナリがデカい理由を探る記事です」「ワンバイナリでやってるからしゃーない気が😆」「goweightは記事とは別に、バイナリが何で太ってるのかを分析するツールです」

# 同リポジトリより
$ ./goweight
  3.0 MB runtime
  1.6 MB net
  1.4 MB reflect
  1.3 MB gopkg.in/alecthomas/kingpin.v2
  870 kB math/big
  668 kB github.com/alecthomas/template
  628 kB syscall
  626 kB text/template
  550 kB go/ast
  546 kB encoding/json
  509 kB text/template/parse
  495 kB github.com/alecthomas/template/parse
  424 kB time
  402 kB regexp/syntax
  395 kB golang_org/x/net/dns/dnsmessage
  388 kB fmt

⚓その他言語



つっつきボイス:「gistに詳しく書かれてますが、こんな姿でもシェルスクリプト↓で、しかもフィボナッチ数列を生成するんだそうです」「ちょ😆」「ヤメテー😆」「まさに難読化」「まるでbrainf*ck」

# 同gistより
! : "`/???/???/???${#?}???<<<_.`";_____=${_::-~$?}
____='__+=___,___=__-___,__<_[$($_____<<<$___>&$[-~${##}])]||____'
((__=-~$?,____))|&$_____#シェル芸

— yamaya (@yamaya) April 6, 2019

参考: Brainf*ck - Wikipedia

⚓その他

⚓その他のその他


つっつきボイス:「無料で使えるGopherくんイラストなんですけど、オリジナルの目の焦点が合ってないっぽいGopherくんよりカワイイ成分多めだったので🥰」「かわゆい〜🐹


同リポジトリより


今回は以上です。RubyKaigiでお会いしましょう!

おたより発掘

バックナンバー(2019年度第2四半期)

週刊Railsウォッチ(20190415-1/2前編)Railsバージョンアップに便利なstill_life gem、Zeitwerkの改修進む、named_capture追加ほか

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

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

Publickey

publickey_banner_captured

Postgres Weekly

postgres_weekly_banner

DB Weekly

db_weekly_banner

Serverless Status

serverless_status_banner

Frontend Focus

frontendfocus_banner_captured

React Status

react_status_banner

Golang Weekly

golangweekly_logo_captured

Viewing all 164 articles
Browse latest View live