Tech Racho エンジニアの「?」を「!」に。
  • Ruby / Rails関連

週刊Railsウォッチ(20171124)GitHubにセキュリティアラート追加、RailsでVue.jsを使う、Railsテスト本2種、node-pruneで瞬間クリーンアップほか

こんにちは、hachi8833です。

Rails: 今週の改修

今週もcommit差分から見繕いました。

klass.all高速化のため不要なspawnを抑制

# activerecord/lib/active_record/scoping/default.rb
              # The user has defined their own default scope method, so call that
               evaluate_default_scope do
                 if scope = default_scope
-                  (base_rel ||= relation).merge(scope)
+                  (base_rel ||= relation).merge!(scope)
                 end
               end
             elsif default_scopes.any?
               base_rel ||= relation
               evaluate_default_scope do
                 default_scopes.inject(base_rel) do |default_scope, scope|
                   scope = scope.respond_to?(:to_proc) ? scope : scope.method(:call)
-                  default_scope.merge(base_rel.instance_exec(&scope))
+                  default_scope.merge!(base_rel.instance_exec(&scope))
                 end
               end
             end

つっつきボイス:mergemerge!に変わったのか」

ActiveRecord::SpawnMethodsを見るとmergespawnがありました。

# actionpack/lib/action_controller/metal/strong_parameters.rb#L718
    def merge(other)
      if other.is_a?(Array)
        records & other
      elsif other
        spawn.merge!(other)
      else
        raise ArgumentError, "invalid argument: #{other.inspect}."
      end
    end

    def merge!(other) # :nodoc:
      if other.is_a?(Hash)
        Relation::HashMerger.new(self, other).merge
      elsif other.is_a?(Relation)
        Relation::Merger.new(self, other).merge
      elsif other.respond_to?(:to_proc)
        instance_exec(&other)
      else
        raise ArgumentError, "#{other.inspect} is not an ActiveRecord::Relation"
      end
    end

after_bundleコールバックを非推奨化

fbd1e98からRailsのプラグインで生成時にbundle installが実行されなくなったため、
after_bundleコールバックはbundle後に実行されなくなった。
このコールバック名と実際の動作が合わなくなったので削除すべきと考える。
#60c550より

# railties/lib/rails/generators/rails/plugin/plugin_generator.rb
       def run_after_bundle_callbacks
+        unless @after_bundle_callbacks.empty?
+          ActiveSupport::Deprecation.warn("`after_bundle` is deprecated and will be removed in the next version of Rails. ")
+        end
+
         @after_bundle_callbacks.each do |callback|
           callback.call
         end

つっつきボイス: 「Rails 5のプラグインって何だろうと思ったら、Railsガイドに書いてあった」

参考: Gem、Railtieプラグイン、Engine(full/mountable)の違いとそれぞれの基礎情報

ActiveStorageルーティングの一部でオプションが無視されていたのを修正

# activestorage/config/routes.rb
Rails.application.routes.draw do
   get "/rails/active_storage/blobs/:signed_id/*filename" => "active_storage/blobs#show", as: :rails_service_blob, internal: true

-  direct :rails_blob do |blob|
-    route_for(:rails_service_blob, blob.signed_id, blob.filename)
+  direct :rails_blob do |blob, options|
+    route_for(:rails_service_blob, blob.signed_id, blob.filename, options)
   end

-  resolve("ActiveStorage::Blob")       { |blob| route_for(:rails_blob, blob) }
-  resolve("ActiveStorage::Attachment") { |attachment| route_for(:rails_blob, attachment.blob) }
+  resolve("ActiveStorage::Blob")       { |blob, options| route_for(:rails_blob, blob) }
+  resolve("ActiveStorage::Attachment") { |attachment, options| route_for(:rails_blob, attachment.blob, options) }
...

つっつきボイス: 「おー、随分抜けてたなー」「テストが見当たらないけど後から足すのかな?」

Rails

書籍『Everyday Rails Testing with RSpec』2017年版の第2章が更新

leanpub.com/everydayrailsrspecより

見逃していましたが、同書は6月にSpec 3.6とRails 5.1向けにメジャーアップデートしていて、それがさらに更新されたということです。英語版を購入した方は無料で更新版をダウンロードできます。

書籍『Rails 5 Test Prescriptions』(ベータ版)


pragprog.comより

Rails 5.1+Webpackに対応しているそうです。おおよその目次は以下です。

  • TDDの寓話
  • TDDの基本
  • RailsのTDD
  • テストを良くする要素
  • モデルのテスト(書籍サンプルPDF
  • テストで日時を扱う
  • ダブル(double)をモックやスタブとして使う
  • CapybaraとCucumberを使った結合テスト
  • JavaScriptの結合テスト
  • JavaScriptの単体テスト
  • Railsの表示要素のテスト
  • MiniTest
  • セキュリティのテスト
  • トラブルシューティング/デバッグ
  • テストの高速化
  • レガシーコードのテスト

つっつきボイス: 「prescription: 処方箋ですね」「このあたりの本、輪読会に向いてそう」

RSpecからController specを消し去る(RubyFlowより)


everydayrails.comより


つっつきボイス: 「コントローラを薄くしてコントローラのテストも薄くするという考え、とても同意できる: コントローラに業務ロジックとか書かなければ、コントローラのテストはアクセスチェックで十分なはず」「昔コントローラのテストをどこまで書くべきか悩んでました」

[Rails] RSpecをやる前に知っておきたかったこと

RailsアプリをAWS Elastic Beanstalkにデプロイする


syndicode.coより


つっつきボイス: 「Elastic Beanstalkってどんなサービスだったかしら」「論理VMレベルのDockerに近いかも: Vagrantに近いと言えばイメージ近いかな」「この記事ではRailsのSECRET_KEY_BASEも設定してますね」「最近の作って捨てるポリシのインフラ界隈では、configはハードコードせずに環境変数としてinjectionせよ、というのがベストプラクティスになりつつあるので、実はRailsのsecret.key.enc方式はそうした流れに逆行しているかな」

参考: よくある質問: AWS Elastic Beanstalk

FastRuby.io: 古いRailsのアップグレード相談サイト

以前のウォッチでもご紹介したhttps://www.upgraderails.com/とちょっと似た感じです。upgraderails.comはアップグレード請負が全面に出ていますが、fastruby.ioは「まずはご相談」という雰囲気で、無料ガイド(PDF)も配布しています。

www.upgraderails.comより

JRubyだとTime.iso8601Time.parseの14倍速かった

早すぎる最適化もたまにはいいことがあるという主旨です。

# 同記事より
# JRuby 9.0.4.0           
Warming up --------------------------------------
           No format     1.111k i/100ms
          ISO format    18.031k i/100ms
Calculating -------------------------------------
           No format     16.364k (± 3.3%) i/s -     82.214k
          ISO format    237.077k (± 4.2%) i/s -      1.190M

Comparison:
          ISO format:   237076.7 i/s
           No format:    16364.4 i/s - 14.49x slower

つっつきボイス: 「CRubyだとTime.iso8601に変えても3%しか速くならなかったそうです」「へー、JavaのDateライブラリはとても使いにくくて何度も変わってたのに」「Rubyのパースは柔軟だけどその分遅いのかな」

参考: wikipedia-ja ISO8601

RailsでVue.jsを使う


classandobjects.comより

RailsにVue.jsを導入する手順の解説です。

# 同記事より
app/javascript/
├── components
│   ├── App.vue
│   └── shared
│       └── csrf.vue
├── packs
│   ├── devise
│   │   └── registrations
│   │       └── new.js
└── views
    ├── devise
        └── registrations
            └── new.vue

つっつきボイス:rails new myapp --webpack=vueでVue.jsインストールできるのか↓」「5.1からこれ使ってました」「=jqueryはないというかなしさ」

# Intalling vue
# Rails 5.1+ new application
rails new myapp --webpack=vue

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

DHHインタビュー(Ruby Weeklyより)


lifehacker.comより

子供の頃プログラミングを学ぼうとして何度も挫折し、もっぱらゲームに勤しんでたそうです。


つっつきボイス: 「いつもの写真と違いますね」「これも最近の写真じゃないだろうなきっと」「今もTextMateが好きらしいです」「TextMateはPHP時代にかなり長い間使ってたけど、日本語サポートが残念すぎて半角日本語フォントを自作ビルドして入れるとかしないとまともに表示できなかったり、TextMate3がいまいちだったりした辺りで乗り換えたな」

Service Object支援gem 2本立て

active_interaction: ビジネスロジックをCommandパターンで書くgem

# AaronLasseigne/active_interactionより
class BooleanInteraction < ActiveInteraction::Base
  boolean :kool_aid

  def execute
    'Oh yeah!' if kool_aid
  end
end

BooleanInteraction.run!(kool_aid: 1)
# ActiveInteraction::InvalidInteractionError: Kool aid is not a valid boolean
BooleanInteraction.run!(kool_aid: true)
# => "Oh yeah!"

waterfall: チェインを意識した関数型的Service Object gem(Ruby Weeklyより)

apneadiving/waterfallより

# apneadiving/waterfallより
class FetchUser
  include Waterfall

  def initialize(user_id)
    @user_id = user_id
  end

  def call
    chain { @response = HTTParty.get("https://jsonplaceholder.typicode.com/users/#{@user_id}") }
    when_falsy { @response.success? }
      .dam { "Error status #{@response.code}" }
    chain(:user) { @response.body }
  end
end

Flow.new
    .chain(user1: :user) { FetchUser.new(1) }
    .chain(user2: :user) { FetchUser.new(2) }
    .chain  {|outflow| puts(outflow.user1, outflow.user2)  } # report success
    .on_dam {|error|   puts(error)      }                    # report error


apneadiving/waterfallより


つっつきボイス: 「この間のRails開発のコツ記事にもありましたが、みんなService Objectをどうにかしたいんだなと感じました」「このWaterfallというgemの名前は機能に即してて好き: 図もわかりやすいし↑」「ワークフローっぽいですね」「JSのPromiseをちょっと連想しました」
「ところでService ObjectのServiceという言葉、意味が広すぎてあまり好きじゃないです: Domain Objectと呼んで欲しかった」「Domainも相当意味が広い気がしますね」

参考: 混乱しがちなサービスという概念について

Ruby trunkより

リクエスト: Kernel#ppをデフォルトで有効にして欲しい

賛成が集まりつつあります。


つっつきボイス:#ppって使ったことないけど何の略?」「Kernel#pp(pretty print)は普通によく使いますね: printfデバッグ的なことをするときとか」「確かにデフォルトでrequireされるようになったらありがたい」

Ruby

RubyのChain of ResponsibilityパターンとProxyパターン(RubyFlowより)

rubyblog.proより

Rubyデザインパターンの記事2本です。ProxyパターンではVirtual proxy/Protection proxy/Remote proxy/Smart referenceの4つの応用例が示されています。

Rubyでワーカープールを実装する(Ruby Weeklyより)

# 同記事より
worker_1 got #<Proc:0x007fc35a132d18@worker_pool_2.rb:40 (lambda)>
worker_0 got #<Proc:0x007fc35a130a40@worker_pool_2.rb:89 (lambda)>
worker_3 got #<Proc:0x007fc35a1309a0@worker_pool_2.rb:89 (lambda)>
worker_5 got #<Proc:0x007fc35a130950@worker_pool_2.rb:89 (lambda)>
worker_7 got #<Proc:0x007fc35a1308b0@worker_pool_2.rb:89 (lambda)>
worker_9 got #<Proc:0x007fc35a130810@worker_pool_2.rb:89 (lambda)>
worker_5 got #<Proc:0x007fc35a1305b8@worker_pool_2.rb:89 (lambda)>
# reduced output lines...
worker_4 got #<Proc:0x007fc35a130428@worker_pool_2.rb:89 (lambda)>
worker_6 got #<Proc:0x007fc35a130900@worker_pool_2.rb:89 (lambda)>
worker_2 got #<Proc:0x007fc35a130478@worker_pool_2.rb:89 (lambda)>
worker_1 got #<Proc:0x007fc35a1307c0@worker_pool_2.rb:89 (lambda)>
worker_8 got #<Proc:0x007fc35a130018@worker_pool_2.rb:89 (lambda)>
worker_4 got #<Proc:0x007fc35a1304f0@worker_pool_2.rb:89 (lambda)>
worker_0 got #<Proc:0x007fc35a1306f8@worker_pool_2.rb:89 (lambda)>

つっつきボイス: 「ワーカーというとUnicornとかPumaとか」「そういうソースを追うときに役立ちそうですね」

google_translate_diff: Google翻訳APIで巨大な文の差分だけ翻訳するgem(RubyFlowRuby Weeklyより)

# 同記事より
s = "There are 6 pcs <b>Neumann Gefell</b> tube mics MV 101 with MK 102 capsule. It is working with much difference capsules from neumann / gefell.\nAdditionally…"

GoogleTranslateDiff.translate(s, from: :en, to: :es)

=> # Tokenize

["There are 6 pcs ", :text],                   
 ["<b>", :markup],                             
 ["Neumann Gefell", :text],                    
 ["</b>", :markup],                            
 [" tube mics MV 101 with MK 102 capsule.", :text],            
 ["It is working ... / gefell.\n", :text],     # NOTE: Separate sentence
 ["Additionally…", :text]]                     # NOTE: Also, separate sentence

=> # Load from cache and translate missing pieces

["Ci sono 6 pezzi ", :text],                   # <== cache
 ["<b>", :markup],
 ["Neumann Gefell", :text],                    # <== Google ==> cache
 ["</b>", :markup],                            
 [" Tubi MV 101 con ... ", :text],             # <== Google ==> cache
 ["Sta lavorando cn ... / gefell.\n", :text],  # <== cache
 ["Inoltre…", :text]]                          # <== cache

=> # Join back

"Ci sono 6 pezzi <b>Neumann Gefell</b> Tubi MV 101 con capsula MK 102. Sta lavorando con molte capsule di differenza da neumann / gefell.\nInoltre"

やはりというか、差分翻訳のコンテキストは失われてしまうそうです。


つっつきボイス: 「こういうオレオレ翻訳支援ツールって車輪の再発明がものすごく多い世界: ローカライズ業界では訳文の再利用に翻訳メモリというものをよく使ってるんですが、サイズが大きくなって低品質の翻訳が混じるとどんどん残念になってしまう」

Google Translator Toolkitと翻訳メモリ(ノーカット版) : RubyWorld Conference 2013より

sniffer: 外向きHTTPリクエストのアナライザgem


aderyabin/snifferより

1月足らずで★250超えです。これも含め、最近evilmartians.comがスポンサーになっているgemをときどき見かけます。

# aderyabin/snifferより
require 'http'
require 'sniffer'

Sniffer.enable!

HTTP.get('http://example.com/?lang=ruby&author=matz')
Sniffer.data[0].to_h
# => {:request=>
#   {:host=>"example.com",
#    :query=>"/?lang=ruby&author=matz",
#    :port=>80,
#    :headers=>{"Accept-Encoding"=>"gzip;q=1.0,deflate;q=0.6,identity;q=0.3", "Connection"=>"close"},
#    :body=>"",
#    :method=>:get},
#  :response=>
#   {:status=>200,
#    :headers=>
#     {"Content-Encoding"=>"gzip",
#      "Cache-Control"=>"max-age=604800",
#      "Content-Type"=>"text/html",
#      "Date"=>"Thu, 26 Oct 2017 13:47:00 GMT",
#      "Etag"=>"\"359670651+gzip\"",
#      "Expires"=>"Thu, 02 Nov 2017 13:47:00 GMT",
#      "Last-Modified"=>"Fri, 09 Aug 2013 23:54:35 GMT",
#      "Server"=>"ECS (lga/1372)",
#      "Vary"=>"Accept-Encoding",
#      "X-Cache"=>"HIT",
#      "Content-Length"=>"606",
#      "Connection"=>"close"},
#    :body=> "OK",
#    :timing=>0.23753299983218312}}

つっつきボイス: 「おーハッシュで取れる: アプリの動作確認とかでときどき欲しくなるヤツかも」「pryの中で動かせるのがいいですね」

bundlerでcombinationが原因のバグ


depfu.comより

# 同記事より
[1,2,3,4].combination(2).to_a
 => [[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]]

([1] * 26).combination(13).size
 => 10400600

つっつきボイス:#combinationって数学の順列組み合わせの組み合わせでしたっけ」「ですです」「そういえば#productはRSpecでよく使ってます」「組み合わせが爆発してbundlerがめちゃくちゃメモリ食ったのか」「記事の最後で投げているissue #6114がわかりやすそう」「組み合わせ爆発怖い...」

SQL

PostgreSQLのAdvisory lockとその使い方


shiroyasha.ioより

-- 同記事より
SELECT pg_advisory_unlock(23);
SELECT pg_advisory_unlock(112, 345);

SELECT mode, classid, objid FROM pg_locks WHERE locktype = 'advisory';

 mode | classid | objid
------+---------+-------
(0 rows)

つっつきボイス: 「Advisory lockの訳語って勧告的ロックなのね」「自分でunlockしないといけないあたり、何だかRubyのFiberを思い出しました」「強力すぎて怖いなー」「自己責任迫られるヤツ」

Advisory: 顧問

JavaScript

JavaScriptのコスト(JavaScript Weeklyより)


medium.com/dev-channelより

JavaScriptのどこで時間やリソースを食っているかという調査です。


medium.com/dev-channelより

忙しいJS開発者のためのES6いいとこ取り(JavaScript Weeklyより)


thenewstack.ioより

// 同記事より
var id = `Your name is ${firstName} ${lastName}.`
var url = `http://localhost:8080/api/messages/${id}`
let chicken = {
     name: 'Pidgey',
     jobs:['scratch for worms', 'lay eggs', 'roost'],
     showJobs() {
        this.jobs.forEach((job) => {
        console.log(`${this.name} wants to ${job}`);
       });
    }
};
chicken.showJobs();
//Pidgey wants to scratch for worms
//Pidgey wants to lay eggs
//Pidgey wants to roost

つっつきボイス:
「ES6、バッククォートで式展開できるようになってるじゃないの!」「webpackとbabel使うなら普通に使ってよい機能なんで、最近使い始めた」
「式展開を最初に知ったのはRubyだったんですが、他の言語は?」「PHPで使ったことあった」「Perlにもあったかな」「式展開使ってる言語いろいろありますよ: メジャーなLL系言語ならほぼ持ってるんじゃないかな」

Rubyの文字列連結に「#+」ではなく式展開「#{}」を使うべき理由

「fat arrow => はクロージャなのか」「そういえばCoffeeScriptのアローって->=>の2種類あるんですが、たまに使い分け間違えたりしてその後嫌いになったりすることあった」

参考: CoffeeScript -> と => の違い

AngularとReactとVueのどれがいいの?


objectpartners.comより

// 同記事より: Reactの例
export class Counter extends React.Component {
  render() {
    return <div>
      <h2>Current value is { this.state.value }</h2>
      <button>Increment</button>
    </div>
  }

  increment() {
    this.setState({
      value: this.state.value + 1
    });
  }
}

つっつきボイス: 「みんな悩みますよね」「Reactはrendererがあるのかー: 趣味に合わない」「大きなプロジェクトだとAngularなのかな: TypeScriptだし」

「依存性の注入(DI)」なんか知らなくてもいい(JavaScript Liveより)

面接で「DIとは何かと」いう質問があったのがきっかけで書いた記事だそうです。

// 同記事より
class Knight extends React.Component {
  static propTypes = {
    weapon: PropTypes.any.isRequired
  };
  render() {
    return `🐴 ${this.props.weapon}`;
  }
}

つっつきボイス: 「DIといえばもうJavaでしょう: Rubyだと特に必要を感じないなー」

参考: 猿でも分かる! Dependency Injection: 依存性の注入

⭐node-prune: nodeの不要なファイルを一瞬で除去(GitHub Trendingより)⭐

公開後5日しか経過していないのに★2200超えです。Go言語で書かれています。


github.com/tj/node-pruneより


つっつきボイス: 「これみんな欲しかったヤツでしょうね」「npm使ってるとファイルじゃんじゃん増やされるし」「↑図がすべてを表してるw」「node_moduleはブラックホールより重い、と」

試しに動かしてみると、本当に一瞬で完了しました。

今週の⭐を進呈いたします。おめでとうございます。

CSS/HTML/フロントエンド

CSS Writing Modes Level 3がRecommendation間近?

11/23にCRが更新されていました。


w3.orgより


grid項目のアスペクト比


css-tricks.comより

gridのアスペクト比でお悩みの方向けです。

See the Pen Aspect Ratio Boxes Filling by Chris Coyier (@chriscoyier) on CodePen.

その他

GitHubにセキュリティアラート機能が追加

GitHubのPublicなリポジトリでInsights > Code Dependencyを表示するとセキュリティアラートが表示されるようになりました。現時点ではJavaScriptとRubyが対象です。


つっつきボイス: 「これいいなー」「何年も前にGitHubに放置していた自分のリポジトリで見てみたらどっと出てた...」「gem使う前にInsights > Code Dependencyチェック、が合言葉」

開発者にとって重要な5つの問題解決スキル

dev.to記事です。


dev.toより

  1. 大きくて複雑な目標をシンプルな目標に分割できる
  2. 並列を考えられる
  3. 抽象化できる(やりすぎないこと)
  4. 既存のソリューションを再利用できる
  5. データフローに即して考えられる

つっつきボイス: 「いい感じかつ実用的かも」

tmuxinator: tmuxセッションを簡単に作れる(GitHub Trendingより)


github.com/tmuxinator/tmuxinatorより

★7200超えです。


つっつきボイス: 「BPS社内は確かtmux派とbyobu派がいましたね」「(GNU) screen派もいたはず」「自分は使ってないなー」

qt: Go言語とQtバインディングでマルチプラットフォームアプリ


github.com/therecipe/qt/wiki/Galleryより

★3200超えです。WidgetとQMLのどちらでも動きます。
これが本当なら、同一ソースからWin/Mac/iOS/Androidなど向けアプリを一気にビルドできますね。Qtの商用ライセンス料と、Store登録の面倒臭さが何とかなるといいのですが。


つっつきボイス: 「Qtってキューティーじゃなくてキュートって発音するんじゃなかったでしたっけ?」「あー、そうでした(今初めて発音した...)」

ちょっと手元で動かしてみようと思ったのですがまだサンプルをセットアップできていません。QtのSDK削除するんじゃなかった...

primitive: 画像を幾何学図形の集まりで再現する(GitHub Trendingより)


github.com/fogleman/primitiveより

★8000近くあります。TechRachoの画像加工でも早速使っています。

shapecatcher.com: 手書きした文字に似ているUnicode文字をリストアップ

絵文字を絵で検索することもできます。

番外

仕事でしかコード書かない開発者ってどうよ?

記事というよりツイート並の短さですね。怒涛のようにレスが付いています。

どうしてこうなったんだっけ

プラセボは腰痛にも効く


つっつきボイス: 「そういえばデュアルディスプレイやめたら肩こり治ったんですよ」「マジで?!」「どうも首を左右に動かしていたのが肩によくなかったのかも」

おめでとうございます!


今週は以上です。

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

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

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

Ruby Weekly

RubyFlow

160928_1638_XvIP4h

Postgres Weekly

postgres_weekly_banner

Frontend Weekly

frontendweekly_banner_captured


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。