Tech Racho エンジニアの「?」を「!」に。
  • 開発

週刊Railsウォッチ(20170818)RailsとYarnでTypeScript、Rails新コミッタにkamipoさんも、CSVにSQLクエリをかけられるツールほか

こんにちは、hachi8833です。octotreeといういいものがあるのを知らずにGitHubで毎回真正面からディレクトリ掘ってました。

お盆明けのRailsウォッチ、今週は軽めの構成です。それではいってみましょう。

Rails: 🎉🎉 新committerが3名: kamipoさんも(Rails公式ニュースより)🎉 🎉

committer入りした3名様、お疲れさまです&おめでとうございます!

Ryuta Kamizono: Ryuta is a top 20 all-time Rails contributor with work going back to 2013. He's got his fingerprints all over the framework, but have in particular been helping with Active Record. Ryuta lives in Tokyo.


つっつきボイス: 「Ryutaだと一瞬だけピンとこないですね」「2013年からトップ20って、凄!」「DHHが手放しで褒めちぎってるw」「fingerprintsは技術用語かつ比喩ですね」「PostgreSQL周りといえばkamipoさんという印象」
「contributorとcommitterの違いって気にしてなかったけど、contributor→committerなのか」

「いつもの話ではあるけど、こうやって陽の光の当たるところでcontributorをねぎらう習わしはRailsやRubyのいいところですね」


オープンソースに限らず、面倒な仕事をやってくれているありがたい人に大勢の人が無意識に甘えてしまい、リクエストや苦情が押し寄せて台無しになってしまうのをよく見かけますね。この残念な現象にうまい名前を付けたい気持ちです。

Rails: 今週の改修

5.1.3リリース直後&夏休みのためかRails公式の改修情報も夏枯れなので、気になるものをいくつかチェックしてみました。網羅的なのはy-yagiさんにおまかせして、閉じてないissueを少し見てみました。

進行中→merge: adapter_is?でアダプタ名を返すよう修正

# https://github.com/rails/rails/pull/30287 より
    def adapter_is?(*adapter_class_symbols)
-      adapter = ActiveJob::Base.queue_adapter.class.name.demodulize.chomp("Adapter").underscore
-      adapter_class_symbols.map(&:to_s).include? adapter
+      adapter_class_symbols.map(&:to_s).include? ActiveJob::Base.queue_adapter_name
     end

?付きメソッドが論理値でないものを返しているように一瞬見えたのですが、#include?の結果は論理値ですね。失礼しました。


つっつきボイス: 「昼間ツイートで見かけたのが、もうmergeされてるw」

進行中→解決: スコープ付きのbelongs_tojoinされない

再現コードがみっちり付いていますが、長いので一部だけ。

# https://github.com/rails/rails/issues/30284 より
class Photo < ActiveRecord::Base
  belongs_to :photographer
  belongs_to :imageable, polymorphic: true

  belongs_to :post,
             -> { joins(:photos).where(photos: { imageable_type: 'Post' }) },
             foreign_key: :imageable_id,
             optional: true

  belongs_to :comment,
             -> { joins(:photos).where(photos: { imageable_type: 'Comment' }) },
             foreign_key: :imageable_id,
             optional: true

  scope :attached_to_posts,    -> { joins(:post) }
  scope :attached_to_comments, -> { joins(:comment) }
end

つっつきボイス: 「むむ、belongs_toscopeの両方でjoin使ってるのか」「polymorphicのbelongs_toって普通はas: :imageableみたいに書くんじゃなかろうか」「普通こういう書き方はしないですねー」


翌日見てみると、kamipoさんが回避方法↓を示して解決→closeしていました。

# https://github.com/rails/rails/issues/30284#issuecomment-323073496 より
class Photo < ActiveRecord::Base
  def post
    if association(:post).loaded?
      super
    else
      imageable if imageable_type == "Post"
    end
  end

  def comment
    if association(:comment).loaded?
      super
    else
      imageable if imageable_type == "Comment"
    end
  end
end

今気づきましたが、再現コードがcontrived(わざとらしい)と形容されているので、やはり問題再現のための無理やりコードですね。

RubyでStrong Parametersをfunctionalに書いてシンプルにしてみたった


blog.martinosis.comより

最近のRailsでおなじみのstrong parametersをあえて関数型言語っぽく書いてみたという記事です。

# いつものstrong parameters
params.permit(:name, 
              {:emails => []}, 
              :friends => [ :name, 
                            { :family => [ :name ], 
                              :hobbies => [] }])
# 関数型風味
friend = hash_of.(name: scalar,
          family: hash_of.(name: scalar),
          hobbies: array_of.(scalar))
hash_of.({ name: scalar,
           emails: array_of.(scalar),
           friends: array_of.(friend)})

つっつきボイス: 「確かに下のコードの方がわかりやすくはある」「宣言的だし」「.()でつなぐ記法は慣れてないとドキッとするw」「脳をカリー化に切り替えるコストがなかなか大きいw」「なおJavaでreflectionやるのはとってもつらいでござるよ」
「でもまあここまでするんだったら、strong parametersのためのもうちょっとマシなDSLを定義した方がよいとは思う」「functionalって日本語にすると長ったらしくなって面倒ですw: 関数型言語風とか」
「Rubyのカリー化はProc#curryか」「そういえば昨年のTechRacho記事でRubyのカリー化の話題出てましたね、私がTechRachoに参加する直前ぐらい」

クソコードを変態コードにしてやった【勉強会報告】

そこからJSON Schemaの話題になりました。

「strong parameterをJSON Schema↓あたりで静的に書ける方が好きだなー: 誰が見てもわかるし」「コメント書けるJSON5もはよ」

{
    "title": "Person",
    "type": "object",
    "properties": {
        "firstName": {
            "type": "string"
        },
        "lastName": {
            "type": "string"
        },
        "age": {
            "description": "Age in years",
            "type": "integer",
            "minimum": 0
        }
    },
    "required": ["firstName", "lastName"]
}

参考

箸休め: 夏休みにRailsコミット観察日記はいかが

Rails初心者やジュニア開発者、学生さんが一皮むける方法のひとつとして、y-yagiさんのrails commit log流し読み風にRailsコミット観察日記的なものをつけてみるといいかもしれません。最初のうちは「この行が何なのかさっぱりわからん」「すっげえ量」「みんなどうやって追っかけてるんだろう」ぐらいで十分だと思います。

いきなりRailsのcommitを開くと気後れするので、Watchボタン↓で更新メールを自分に飛ばすとか、RSSで読むとかRSSをSlackに飛ばすとかで楽しましょう。

日記をどこにどう書くかはまったく好き好きでよいと思いますが、私としては適当なブログサイトに観察日記を独り言っぽく書き捨てるのがおすすめです。ローカルPCや非公開ブログより、多少人から見える方が継続しやすいようです。

検索で見つかるのは恥ずかしいという内気な方は、タイトルを日付だけにしておけば検索エンジンのクローラーもあきれて通り過ぎてくれますので安心です。ブログが観察日記専用なら重複コンテンツでクローラーからペナルティ喰らっても問題ありません(会社のブログでやるときには重複コンテンツにならないよう注意しましょう)。

y-yagiさんのように雨の日も風の日も盆暮れ正月も更新を続けるのはめったにできることではありませんが、はずみに乗って週一程度コミットを眺める習慣が付いたらそれだけでも儲けものですね。

TypeScriptを新しいRails + Yarnで使う手順


yizeng.meより

お盆のせいか、TypeScriptをRailsで使いたいという声が遠くの方からかすかに聞こえてきたので、新しめの記事を探してみました。

# yizeng.me より
class HelloWorld {

    private name: string;

    constructor(name: string) {
        this.name = name;
    }

    print() {
        alert(`Hello World, ${this.name}!`);
    }
}

new HelloWorld('John Doe').print();

つっつきボイス: 「最近のRailsでTypeScriptしたい人には助かりそう」「TypeScriptで型定義しておけばどんなによかったろうというRails案件があったでござるよ...」「TypeScriptそんなに難しくないですヨ」
「以前だったらtypescript-rails gemみたいなのをインストールするところですね」「今のRailsはJSをgemでインストールしない方向に変わったから、*-railsみたいなJS系gemは基本使わない」
CoffeeScriptはRailsビューにインラインでも書けるけど、TypeScriptもインラインでビューに書けるのかな?できそうだけど」


最近はちょっぴり落ち着き気味らしいTypeScript人気ですが、今さらのように少し眺めてみると、ブサメンコードでの殴り合いが日常茶飯事の修羅JS界では異色の端正さと、ガツガツマウントを取りに行かない育ちのよさをほんのりと漂わせていて、なかなかのイケメンコードに見えてきた気がします(個人の感想です)。どうか悪魔合体しませんように。

function classDecorator<T extends {new(...args:any[]):{}}>(constructor:T) {
    return class extends constructor {
        newProperty = "new property";
        hello = "override";
    }
}

@classDecorator
class Greeter {
    property = "property";
    hello: string;
    constructor(m: string) {
        this.hello = m;
    }
}

console.log(new Greeter("world"));

参考

Rubyで連結リスト(RubyFlowより)

アルゴリズムの授業でおなじみの連結リストをRubyで実装して配列と比較してみたそうです。


つっつきボイス: 「ベンチマークもやってるな」「arrayと連結リストで計算量の比較もやってますね」「ほむほむ、今pryで適当にarrayにappendしてみたけど、相当な回数繰り返してもarrayのobject_id変わらないなー: Rubyだとどのぐらいappendしたらarrayオブジェクトが再生成されるかな...れれ?」

a = [1,2,3,4,5,6,7,8]

def insert(arr, item, pos)
  tmp      = arr[pos]
  arr[pos] = item

  arr.replace(arr[0..pos] + [tmp] + arr[pos+1..-1])
end

insert(a, 99, 3)
p a

「あー、このコード、むりやりarrayを再生成させておる↑これはいかんなー」「それじゃ比較の意味ないですね」
「そもそも、Rubyのような高度な言語でデータ構造とかメモリアロケーションみたいなテーマを扱うのは無理ある」「確かに、Rubyみたいに最適化の進んだ言語でアルゴリズムを再現しても、内部を知らないと参考にならなさそう」「やるならもっとマシン寄りの言語でね♡」

問題解決に再帰を使ったの久しぶりすぎ

再帰関数がうまくはまった事例を紹介する記事です。


blog.arkency.comより

# http://blog.arkency.com/that-one-time-i-used-recursion-to-solve-a-problem/ より
  class Calculator
    def initialize(current_time:, event_starts_time:)
      @current_time = current_time
      @event_starts_time = event_starts_time
      @eb = ExponentialBackoff.new(12.hours, 1.month)
    end

    def compute
      sub_compute(@current_time, @event_starts_time, 0).sort
    end

    private

    def sub_compute(left_boundary, right_boundary, current_level)
      duration = @eb.interval_at(current_level).seconds
      available_time = right_boundary - left_boundary
      if available_time >= 3*duration
        [
          new_left  = left_boundary  + duration,
          new_right = right_boundary - duration,
        ] + sub_compute(new_left, new_right, current_level+1)
      elsif available_time >= 2*duration
        [
          left_boundary + available_time/2,
        ]
      else
        []
      end
    end
  end

つっつきボイス: 「再帰って今でもプログラミングの授業で教えますよね?」「もちろん」「うんと昔に初めて再帰アルゴリズムを知ったときはすごく感激したんですが、その後『再帰は遅い』みたいな感じで避けられてる感」
「再帰はきれいに問題解決したときはキモチイイけど、使い所に気をつけないと計算量爆発するからw: stack level too deepとか」「無理矢理使うのは感心しないですねー」「parserみたいなところで再帰を使うことはあるので、ActiveRecordのArelあたりでもたぶん使われてる」

RubyのWeakRefライブラリ

テストでガベージコレクション(GC)するのにRubyのWeakRefを使ったそうです。

# http://www.bpietraga.me/ruby-weakref-and-running-garbage-collection-in-tests/ より
require 'rspec'
require 'weakref'

class Modifier; end

class Communicator
  def initialize
    @modifier_object = modifier_object
  end

  def call
    return 'Modifier object was found' if @modifier_object
    'No modifier object found'
  end

  private

  def modifier_object
    ObjectSpace.each_object.find { |obj| modifier?(obj) }
  end

  def modifier?(obj)
    obj.class == Modifier
  rescue
    false
  end
end

RSpec.describe Communicator do
  it "modifier object in object space" do
    GC.start
    WeakRef.new(Modifier.new)
    expect(Communicator.new.call).to eq('Modifier object was found')
  end

  it "modifier object not found" do
    GC.start
    expect(Communicator.new.call).to eq('No modifier object found')
  end
end

つっつきボイス: 「WeakRefって何だろうと思って」「今のRubyのGCってどの方式だったっけ?」
世代別だったと思います」「お、Ruby trunkにあった

私は何となくガベコレと呼んでましたが、ちょい古いかも。

参考

Docker化したRailsアプリをデバッグするコツ10


fuzzyblog.ioより

  • 504 Gateway ErrorはALBがコケてるかも
  • サーバー再起動後にはdev環境でURLをチェック
  • Healthチェック
  • Application Docker Logsをチェック
  • dfでディスク容量チェック
  • htopでCPU/メモリ使用量をチェック
  • コンテナごとのステータスをチェック
  • timber.ioでログを見やすく
  • Error loggerをチェック
  • 結論: コンテナは全部チェックしる!

つっつきボイス: 「うん、どれも普通w」「普通なのでデバッグやるなら当然チェックするということですね」
「ところでこのtimber.ioってカントリー風味でちょっと面白いw」「ちなtimberは『丸太、材木』です」


www.timber.ioより

今気づきましたが、logは本来『丸太』なので、timberと掛け言葉になってるんですね。

「Rubyで#interpose」、自分もやってみた

www.virtuouscode.comより

以前のウォッチでも紹介したClojure's interpose in Rubyを受けた記事です。

# http://www.virtuouscode.com/2017/08/02/riffing-on-interpose-in-ruby/ より
module Interpose
  refine Enumerable do
    def interpose(*)
    end
  end
end

つっつきボイス:refinement使ってる」「Enumerableに品よくパッチを当てるならこうでしょ」
「うん、この解説かなり頑張って書いてある」「以前の記事よりもよく書けてるっぽいです」「step by stepで読み進めると勉強になりそう」

Railsのテストでログを吐かないようにして6%速くなった


jtway.coより

とっても短いのですぐ読めます。

# https://jtway.co/speed-up-your-rails-test-suite-by-6-in-1-line-13fedb869ec4 より
unless ENV['RAILS_ENABLE_TEST_LOG']
  config.logger = Logger.new(nil)
  config.log_level = :fatal
end

つっつきボイス: 「log出力はまあ重いから止めれば速くなるw」「昔のLinux環境なんかで、ファイルサイズが2GBを超えると追記が死ぬほど重くなったりするなんてことも稀に良くあった

Rubygem

public_activity: モデルのactivityをトラックするgem

PAINLESS ACTIVITY STREAMS IN RAILSという記事で見つけました。★2400級で、コミットは現在も増えています。


つっつきボイス: 「おー、これもBPSのmangarebornでほぼ同じことやってたなー: 設計もだいたい似てる」「おおー」「Rails 1ぐらいの時代でしたね」
「こういうgemがあるなら使うのはありだと思いますが、気をつけないとactivityテーブルの容量がめちゃめちゃでかくなるので適当なタイミングで消さないと死にますよw」

batch-loader: N+1問題用gem

図の書き方が面白かったのと、batch-loaderという名前でわかってもらえるのかが気になったので。

# https://github.com/exAspArk/batch-loader より
def load_posts(ids)
  Post.where(id: ids)
end

posts = load_posts([1, 2, 3])  #      Posts      SELECT * FROM posts WHERE id IN (1, 2, 3)
                               #      _ ↓ _
                               #    ↙   ↓   ↘
users = posts.map do |post|    #   U    ↓    ↓   SELECT * FROM users WHERE id = 1
  post.user                    #   ↓    U    ↓   SELECT * FROM users WHERE id = 2
end                            #   ↓    ↓    U   SELECT * FROM users WHERE id = 3
                               #    ↘   ↓   ↙
                               #      ¯ ↓ ¯
puts users                     #      Users

つっつきボイス: 「お、この間のTechRacho記事↓で取り上げてたrender_async gemとコンセプトがよく似てる: Ajaxではなくビューのレベルでやるところが違うくらいで」

Rails: render_async gemでレンダリングを高速化(翻訳)

「めちゃめちゃでかいデータセットに対して扱うとき、データをlazyに小分けしてビューで逐次レンダリングして、表示できるものから先にデータを返していく」「なるほど、そう思うと上のベルトコンベア写真もコメントの図もbatch-loaderという名前も一貫してますね」
「最近のブラウザは賢くて、レスポンスが完結してなくてもrenderingできるので」
「記事もいいですねー」

JavaScript

pretty-algorithms: アルゴリズム集


jiayihu/pretty-algorithmsより

二分木探索などの基本的なアルゴリズムをJavaScriptで書いています。ちょっと珍しい気がしたので。


つっつきボイス: 「大学の先生が授業で使ったりするのに便利かな」「Yahoo知恵袋で必死で答えを教えてもらおうとしている学生とかにもw」

RDBMS

2017年後半は各種RDBMSのメジャーアップデートがリリースラッシュ

morimorihogeさんがSlackに投下した記事で、2017年後半はRDBMSのメジャーリリース予定が目白押しだったことを思い出しました。

ひとまずリリースノートをリンクしておきます。皆さまもお備えあれ。

Trdsql: CSVにSQLでクエリをかけられるツール

morimorihogeさんがSlackに投下してくれました。CSVやタブ区切りテキストなどにSQLクエリをかけられるというのがちょっと驚きです。

$ trdsql "SELECT c2,c1 FROM test.csv"
Orange,1
Melon,2
Apple,3

番外

MapChart: プレゼン作成に便利な白地図

国や地域の色の塗り分けをかっこよくやってくれます。プレゼンがはかどります。


mapchart.netより


つっつきボイス: 「おおーこれ凄い!」「日本の都道府県とかも塗り分けできるし」「夏休みの宿題がはかどりそう」「Googleマップくさくないのが新鮮」「世界征服もはかどる」「これでAPIがあればなー」

今日も元気にP≠NP

暑中見舞い代わりに


今週は以上です。

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

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

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

Rails公式ニュース

RubyFlow

160928_1638_XvIP4h


CONTACT

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