Quantcast
Channel: hachi8833の記事一覧|TechRacho by BPS株式会社
Viewing all articles
Browse latest Browse all 1759

週刊Railsウォッチ(20180910)公開つっつき会#2、RSpecは何を参考にするか、イベントソーシング、marginalia gem、負荷テストツールvegetaほか

$
0
0

こんにちは、hachi8833です。今回の「公開つっつき会 第2回」にお集まりいただいた皆さま、ありがとうございました!おかげさまで盛況のうちに終わり、懇親会も盛り上がりました🍻

  • 各記事冒頭には⚓でパーマリンクを置いてあります: 社内やTwitterでの議論などにどうぞ
  • 「つっつきボイス」はRailsウォッチ公開前ドラフトを社内有志でつっついたときの会話です👄
  • お知らせ: 来週9/17と9/23は月曜祝日のため、週刊Railsウォッチはお休みをいただきます🙇。次回は10/1(月)となります

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

⚓ActiveJobにretryやdiscardなどのフックを追加

# activejob/lib/active_job/logging.rb#L91
+       def enqueue_retry(event)
+         job = event.payload[:job]
+         ex = event.payload[:error]
+         wait = event.payload[:wait]
+          error do
+           "Retrying #{job.class} in #{wait} seconds, due to a #{ex.class}. The original exception was #{ex.cause.inspect}."
+         end
+       end
+        def retry_stopped(event)
+         job = event.payload[:job]
+         ex = event.payload[:error]
+          error do
+           "Stopped retrying #{job.class} due to a #{ex.class}, which reoccurred on #{job.executions} attempts. The original exception was #{ex.cause.inspect}."
+         end
+       end
+        def discard(event)
+         job = event.payload[:job]
+         ex = event.payload[:error]
+          error do
+           "Discarded #{job.class} due to a #{ex.class}. The original exception was #{ex.cause.inspect}."
+         end
+       end

元の#33740の実装↓より上の実装↑の方がよいとのことです。

      def before_retry(*filters, &blk)
        set_callback(:retry, :before, *filters, &blk)
      end

つっつきボイス:「(ゲストに)ところで皆さんActiveJobって使ったことあります?」「名前ぐらいは」「まだです〜」

参考: Active Job の基礎 | Rails ガイド

「ざっと説明すると、ActiveJobはRails 4.2で導入された機能で、それまでResqueSidekiqdelayed_jobなどでまちまちに行われていた非同期処理を、ジョブの抽象表現として提供するのが目的です」「ただしActiveJob自体には定期時間になったら実行するといった機能はありません: ActiveJobは一種の抽象化クラスとして、実際のジョブはそのバックエンドにSidekiqなりdelayed_jobを配置してそちらで動かすことになります」

「で今回の修正はというと、ジョブのリトライ時などにフックをかけられるようにしたということのようですね」「…だいたいChangelogに書いてあるとおりかも: とりあえずログに出してくれるようになったし」「あー確かにリトライのときとかログに出したい😊」「…リトライでログが出てくれれば、何だか無限ループっぽいときにも一瞬でわかるけど、ログがないとみっちり調べないとわからないし🧐

「ちなみにコードにも出ているActiveSupport::Notificationsは、ActiveSupport標準のpub/subの仕組みですね😋: Railsのあちこちで使われているヤツ」

参考: ActiveSupport::Notifications

「なおActiveJobはまだ足りない機能が結構あるんで、実はあんまり使ってない😆」「おほ😆」「使ってみるとわかるんですけど、たとえばSidekiqだったらできることができなかったり: SidekiqってそのまたバックエンドにRedisがあってRedisのブロッキング系コマンドが使えるので『何時間後に実行』みたいなスケジューリングができるんですけど、ActiveJobにその抽象化がないので、ActiveJobでジョブを作るとそういうのができない😅」「ActiveJobで試しに書いたものの結局Sidekiqを生で使いましたね」「このあたりもうひと頑張りして欲しいところ」

参考: リスト型 — redis 2.0.3 documentation — ブロッキング系コマンドの解説

「BasecampではActiveJobってどう使ってるんだろう?🤔」「あ、スケジューリングとかしない普通の非同期ジョブならActiveJobでできます: 本来単なる非同期ジョブとスケジューリングは別の話ですし」「スケジューリングとかはwheneverとか使ってもできますが、cronが必要だし、RedisやSidekiqならそういうのもできるからそっちでやっちゃいますね😎」「まあwheneverよりRedisサーバーを立てる方が面倒ですが☺

⚓マルチDB対応を強化

  1. replicaオプションを追加
  2. config["replica"]をチェックするreplica?HashConfigUrlConfigに追加
  3. configs_forでキーワード引数を取れるようにした
  4. configs_forinclude_replicasのデフォルトをfalseにするキーワード引数を取れるようにした
    同PRより

つっつきボイス:「以前の(Rails 4系前半ぐらいまでの)ActiveRecord::Baseのコネクションってシングルトンだったんですよ: その頃にマルチDBやろうとしたらgemをインストールしないといけなかったんですが、何てgemだったかな…?🤔」「octopus?」「あれは最近ほとんど更新されてない気が」

そういえば以前のウォッチではmulti_dbswitch_pointも挙がってました。

参考: Rails4.2のコネクションプールの実装を理解する - Akatsuki Hackers Lab | 株式会社アカツキ(Akatsuki Inc.)

「それが最近になってconnectionがconnectionsになる形で公式がマルチDBをある程度サポートするようになった」「config["replica"]が入ってきたということは、Railsもますますエンタープライズアプリを志向するようになってきてこういうのが求められるようになったということでしょうね: 実際BPSでも業務系アプリの案件が増えてきてますし」「replicaだからリードオンリー向けの機能ですね」

「ところでマルチDBってやったことあります?」「いやぁまだですね☺」「なかなかハードです😭」「どんなときに使うんでしょう?」「既存のシステムのマスターデータを読みに行かないといけないときなんかがそうですね」「Railsで使うデータベースは、サロゲートキーやidカラムを使うみたいにRailsのルールで作られるんですけど、既存のデータベースは思想が違うのでそういうふうにできていない😎

「マルチDBをサポートするgemにもいくつかパターンがあって、Railsが使うメインのデータベースとは別にサブのデータベースをリードオンリーで使えるものもあれば、複数のデータベースをすべて読み書きできるgemもありますね」「😃」「そしてマルチDBだとたいていマイグレーションがめちゃめちゃハードになるんですねこれが😭

⚓permitted_scalar_filterのアロケーションを削減

Before:
 16199  /Users/rschneeman/Documents/projects/rails/actionpack/lib/action_controller/metal/strong_parameters.r

After:

  2280  /Users/rschneeman/Documents/projects/rails/actionpack/lib/action_controller/metal/strong_parameters.rb
# actionpack/lib/action_controller/metal/strong_parameters.rb#L926
-     def permitted_scalar_filter(params, key)
-       if has_key?(key) && permitted_scalar?(self[key])
-         params[key] = self[key]
+     def permitted_scalar_filter(params, permitted_key)
+       permitted_key = permitted_key.to_s
+
+        if has_key?(permitted_key) && permitted_scalar?(self[permitted_key])
+         params[permitted_key] = self[permitted_key]
        end
-        keys.grep(/\A#{Regexp.escape(key)}\(\d+[if]?\)\z/) do |k|
-         if permitted_scalar?(self[k])
-           params[k] = self[k]
-         end
+       each_key do |key|
+         next unless key =~ /\(\d+[if]?\)\z/
+         next unless $~.pre_match == permitted_key
+          params[key] = self[key] if permitted_scalar?(self[key])
        end
      end

つっつきボイス:「permitted_scalar_filter?」「こんな機能が元からあったんですね」「scalarってスカラー量のこと?」「パラメータータイプでscalarと言うとたいてい『オブジェクトではない』という意味になりますね🧐」「arrayとかhashとかでない、プリミティブな値ってことですね: ここで言うPERMITTED_SCALAR_TYPESは、strong parametersに渡してもいい値のリスト↓ってことか!」

PERMITTED_SCALAR_TYPES = [ String, Symbol, NilClass, Numeric, TrueClass, FalseClass, Date, Time, StringIO, IO, ActionDispatch::Http::UploadedFile, Rack::Test::UploadedFile, ]
api.rubyonrails.orgより

「たとえばuser_idが数値を受け取ることを期待しているのにarrayが渡されて素通しされるといろいろヤバいので、そういうのを防ぐ」「渡された値は必ずPERMITTED_SCALAR_TYPESのどれかになると」「これにないものは明示的に許可しないとrejectされるので、idにうっかりarrayが入ったりしなくなる」「UploadedFileがあることからしてそんな感じですね」「このフィルタ、毎回呼び出されそう」「そのあたりが高速化したということのようだ: ここではキーをgrepするよりeach_key使った方が速いってことかな」

参考: Action Controller の概要 / 4.5 Strong Parameters | Rails ガイド

⚓関連付けで複数形を指定した場合にもinverse_ofできるように修正

# 同PRより
class Post
  has_many :comments
end

class Comment
  belongs_to :post
end

つっつきボイス:「これって以前からできてませんでしたっけ?」「このinverse_of的なヤツ↓がいつの頃からかあって、それを使うとよしなにやってくれるんですけど、自分はこういうのあまり信用してないので基本使わない☺

# activerecord/lib/active_record/reflection.rb#L614
        def automatic_inverse_of
          if can_find_inverse_of_automatically?(self)
            inverse_name = ActiveSupport::Inflector.underscore(options[:as] || active_record.name.demodulize).to_sym
          return unless can_find_inverse_of_automatically?(self)

「これこれ、この記事↓にもあるinverse_of」「これですかー」「その記事で言うとinverse_of: :commentableと書くとcommentableからもarticleを参照できるようになる: 改修はこれにplural associationを指定できるようになったってことでしょうね」

「打てなくてもとりあえず打席に立つ感じでRailsウォッチ毎週読んでるんですが、毎回知らないことだらけ😅」「私もです😅」「自分たちもウォッチで最初Railsのコミットを追い始めた頃はわけわからないことだらけでしたけど、1年もやってるうちにだんだん既視感が育ってきますね」「そうそう」「自分が覚えてなくてもつっつきに参加した誰かが覚えてたりとか: ウォッチをずっとやってきたメリットのひとつ😋

参考: Rails 5 以上 + ActiveRecord での inverse_of オプションの使いどころ - Qiita

⚓aroundコールバックでchanges_appliedが適用されていなかったのを修正

AR::AttributeMethods::Dirtyの修正です。

# activerecord/lib/active_record/attribute_methods/dirty.rb#L167
        def _update_record(*)
-         partial_writes? ? super(keys_for_partial_write) : super
+         affected_rows = partial_writes? ? super(keys_for_partial_write) : super
+         changes_applied
+         affected_rows
        end

         def _create_record(*)
-         partial_writes? ? super(keys_for_partial_write) : super
+         id = partial_writes? ? super(keys_for_partial_write) : super
+         changes_applied
+         id
        end

つっつきボイス:「先週も出てきたDirty周りですね」「来たなw: Dirtyつらい😭」「around系コールバックが行われた時点ではもう保存されてdirtyではなくなっているのに、dirty扱いになってたのはバグ🐞」「『should be cleared』はそういうことですね」「after系ではできてたのにaroundではできてなかったと」「この場合aroundというのは…?🤔」「aroundはbeforeとafterをyieldを使って両方いっぺんにやったのと同じ挙動ですね」「そうだったのか!」「そうそう、aroundの場合もyieldの後で状態が変わってないといけない」「ActiveModelにはこの種のコールバックがいろいろあって、それぞれにbeforeやafterやaroundがあります↓」

参考: Active Record コールバック 「利用可能なコールバック」| Rails ガイド

先週も少し話しましたが、ついでにDirtyも説明: ActiveRecordのattributeを=で更新してsaveしたときに、saveする前の値が取れるというキモい機能🧟‍♂️」「おぉ😳」「更新をsaveしたはずなのに」「直前の値が取れるというのは何かと便利なんですけど、トランザクションがネストするとわけわからなくなりがち😭」「saved_changes?(以前はchanged?)みたいに更新があったかどうかを保存後に調べられるメソッドもあります」

「…監査ログ的な実装で、管理者が変更すると『xxが更新されました』という通知を出すときに使ったりとか」「うーん、自分はそこではDirtyはあんまり使わないかなー🤔: papertrailとかにやらせてデータベース更新のときに取ればいいじゃん派」「自分はgem使わなくてもafter_createの後でDirtyで取ればいいじゃん派」「papertrailの方が自動でできるし楽じゃないですか😆

「ちなみにpapertrailはデータベースの変更履歴を全部取っておいてくれるgem」「ただしattributeが1つでも変わると1レコード追加されるので、これで保存したテーブルは死ぬほどでかくなる: まあひたすらINSERTするだけなんで、重いのはSELECTするときだけなんですけど😆」「…がっつり監査ログを取りたいというよりは、日本語的な意味でログを出したい: 『xxさんがパスワードを変更しました』通知をメールで出すみたいな」「そういう軽めの処理にはDirtyが向いてますね: とりあえずsaveした後にchanged?チェックすればさっとやれる😋

⚓ActionPackのcaching/fragments.rbのアロケーションを削減

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

# actionpack/lib/abstract_controller/caching/fragments.rb#L88
      def combined_fragment_cache_key(key)
        head = self.class.fragment_cache_keys.map { |k| instance_exec(&k) }
        tail = key.is_a?(Hash) ? url_for(key).split("://").last : key
        [ :views, (ENV["RAILS_CACHE_ID"] || ENV["RAILS_APP_VERSION"]), *head, *tail ].compact
         cache_key = [:views, ENV["RAILS_CACHE_ID"] || ENV["RAILS_APP_VERSION"], head, tail]
        cache_key.flatten!(1)
        cache_key.compact!
        cache_key
      end

つっつきボイス:「flatten!(1)って何やるんだっけ…ああ深さの指定か↓」「Rubyを使っていて感激するメソッドのひとつですね: PHPでこういうのやろうとするとダルい😆」「これはプルリクメッセージにあるとおりかな」「ですね: headとtailをsplatの*で取るより深さ1でflatten!(1)する方が速いよと」

# docs.ruby-lang.orgより
# 平坦化の再帰の深さを指定する例。
a = [ 1, 2, [3, [4, 5] ] ]
a.flatten(1)              #=> [1, 2, 3, [4, 5]]

参考: instance method Array#flatten (Ruby 2.5.0)

「こういう最適化してくれないJITしょぼいな😆」「まあまあ、C++みたいなコンパイル言語ならこのぐらいの最適化はやるでしょうけど、Rubyの場合オーバーライド可能ですし大変だと思いますよ」「フラグメントキャッシュは呼び出しすごく多いから、こういう最適化は効きそう」

参考: Rails のキャッシュ: 「フラグメントキャッシュ」| Rails ガイド

⚓番外: attendance: #present?を高速化するgem

#10539で一度はRailsにコミットされたものの、#29400で議論の末Railsから削除されたので、Schneemsさんがgemとして復活させたようです。

# #29400より
USING BLANK?
DEBUG -- :   Comment Exists (0.1ms)  SELECT  1 AS one FROM "comments" WHERE "comments"."post_id" = ? LIMIT ?  [["post_id", 1], ["LIMIT", 1]]

USING BLANK?
DEBUG -- :   Comment Load (0.1ms)  SELECT "comments".* FROM "comments" WHERE "comments"."post_id" = ?  [["post_id", 1]]

つっつきボイス:「LIMITを付けることで高速化できるみたいな話: 前にこのあたりのやりとりを見たことがある気がする」「改修するとメモリに全部読み込まれてしまうので削除されたという流れみたい」「キャッシュから読んだほうが速いこともあるだろうし」

「ところでこのattendance gemの最適化前の"users".*↓って何だかヤバい感😳」「おぉw、LIMITのあるなしとこれの違いが大きそう」

# 同gemより
# gemなし
User.where(github: 'schneems').present?
  User Load (1.5ms)  SELECT "users".* FROM "users" WHERE "users"."github" = $1  [["github", "schneems"]]
=> true

# gemあり
User.where(github: 'schneems').present?
  User Exists (1.2ms)  SELECT  1 AS one FROM "users" WHERE "users"."github" = $1 LIMIT $2  [["github", "schneems"], ["LIMIT", 1]]User.where(githu

⚓Rails

⚓名付けて「Event scout」ルール

イベントソーシングやイベント駆動設計の話です。Arkencyはイベント駆動関連の記事が多いので、コマンドソーシング記事も含めてイベント駆動布教の一環のようです。


つっつきボイス:「確かにイベントソーシングをアプリに適用するのは有効な手段👍」「こういうふうに、cancelのコールバックで処理するのではなくてpublish_event化する形でやると」「これ自体はService Objectのようなので、Service Objectの中をイベントソーシング的に設計するということなんでしょうね😋」「コールバックでやるとどんどん読みにくくなるから、イベントソーシングでやろうというのはわかりみある」

# 同記事より
class CancelOrdersService
  def call(order_id, user_id)
    order = Order.find_by!(
      customer_id: user_id,
      order_id: order_id,
    )
    order.cancel!
    publish_event(order)
  end

  private

  def publish_event(order)
    event_store.publish(
      OrderCancelled.new(data: {
        order_id: order.id,
        customer_id: order.customer_id,
      }),
      stream_name: "Order-#{order.id}"
    )
  end

  def event_store
    Rails.configuration.event_store
  end
end

そもそもScout ruleって何だろうと思ったら、ボーイスカウトガールスカウトの「来たときよりも美しく」のことらしく、それをイベント化でもやろうということのようです。

スカウトルールとは: コードに触ったら、必ず何か小さな改善を施す。単にコードを読んだときやチラ見したときであっても小さな改善を施す。改善はネーミングだったり構造だったりささやかなリファクタリングだったりメソッド切り出しだったり。改善はがっつりやらず、次にそのコードに触る人が少しでも気持ちよくいられるよう心がける。次というのは1か月後かもしれないしもっと後かもしれないが。
同記事より抜粋

「来たときよりも美しく!」「コードを開いたらちょっとでも改善してから立ち去るという」「心構えの話ですね」「リファクタリングのプルリクは別にしておいて欲しいですけどね😆

参考: ボーイスカウト・ルールってご存じですか? - Qiita

⚓Rails以外のWebフレームワーク

8月に開催されたEuRuKo 2018カンファレンスでPhusion社が発表したスピーチの紹介です。

SinatraHanamiTrailblazerPhoenix(これはElixirですが)はこれまでに見かけていましたが、以下は初めて見たので。

  • Padrino: Sinatraに近いが、PadrinoアプリはRailsアプリに変換可能。コミット数と★はこの中で最も多い。
  • Cuba: Rackベースで、Sinatraより速いらしい。現在はメンテされてない。
  • Volt: Opalを使ってクライアントもRubyで書ける。こちらもメンテされてない。


padrinorb.comより


cuba.isより


voltframework.comより

記事では「VoltかSinatraもプロトタイピングでお試しあれ、速度が欲しいならTrailbrazerもね: 古いからといってあなどらないように」と締めくくっています。後半はメタプロのスピーチでした。


つっつきボイス:「関係ないですけど、記事英語タイトルの?!?!の順序って、日本だと普通!?!?ですよね?🤣」「あー🤣」「これってたぶん言語や出版社ごとにスタイルが異なるヤツですね: Phusionはオランダの会社なのでもしかするとオランダ風スタイルなのかも?🤔

そういえばスペイン語は逆さの¿¡という独自の記号がありますが、英文で!を付けていてもスペイン語訳で¡...!を付けないことも多いようです(英文で!が乱用されているだけという気もしますが)。

参考: 逆疑問符 - Wikipedia
参考: 逆感嘆符 - Wikipedia

PhusionPassengerの会社なのでついでにその話: PassengerはRailsのWebサーバーで、Ruby 1.8系の時代に一世を風靡したんですが、後に有料化してgemインストール時にバナーを出すようにしたら、あちこちでボコボコに叩かれたという😆」「そんな歴史があったんですねー😲」「でも最近は復活してますね: 自分もPassengerのRuby Enterprise Editionとか使ってましたし」


phusionpassenger.comより

「有料のPassengerはサポートがあるのが強み: というのも昔はRailsサーバーって落ちるのが当たり前だったので」「マジで🤣」「Rails 3をRuby Enterprise Editionで動かしてた頃なんて、リクエスト数がある程度以上になるとメモリを食いすぎて死ぬというのが当たり前に起きてたし: ちなみに皆さんはいつ頃からRubyやRailsを?」「2年前ぐらいから」「4からですかね」「おおっ健康的!いいですね~🙆🏻‍♂️

「あの頃はRailsをずっと立ち上げてるとRailsワーカー1個で軽く1GBはいってましたしw、リクエスト数が一定数に達したらワーカーを自滅させるPassengerMaxRequestsというあんまりな設定項目↓ありましたし」「ひえ〜🤮

参考: Phusion Passenger ユーザガイド, Apache バージョンPassengerMaxRequests

unicorn-worker-killer、めっちゃ愛用してますが😆」「ワーカーがどうせメモリリークしてるなら数リクエストさばいたら死んでくれという諦めの境地」「潔いですね〜」「二百三高地ですか😆

「こういうの今でも使われてます?『たとえばGCを止めて』↓、適当に増えたら殺すみたいなの」「今はまず使わないっすね: 昔(Ruby 1.8頃)はOobGC入れないと全然ヤバかったけど」「OobGC?」「Out of…何だっけ?」「bandか」「bounceではなかったw」「😆」「OobGCは、要はリクエストが完了するまではGCを動かさないようにするという機能」「だってあの頃は半分以上GCがメモリ食ってて明らかにおかしかったし、止めたら倍早くなったし」「当時は普通にやってましたね〜☺

参考: 例えば GC を止める・Ruby ウェブアプリケーションの高速化 - 2nd life

「話逸れちゃいましたが、Phusionの記事はというと」「PadrinoとかCubaとかVoltという見慣れないRuby Webフレームワークですね」「VoltってJSにある気がするけど」「ElectronがあるぐらいだからVoltもありそう😆」「Voltはクライアント側もOpal使ってRubyで書けると言っています」「皆さんOpalはご存知?」「RubyをJavaScriptで動かせるヤツですよね↓」「正解!🎯productionで使うには度胸が要る、なかなかエグいヤツ」「エグいんですね😆」「RubyKaigiでも確か2年続けてスピーチされてましたね」「Opalは普通にトランスパイルしてる?」「そうですね、変換テーブル使ってて、integerとかはRubyとJSで挙動が違っちゃう😅」「WebAssemblyで動かすとかそういう楽しい話ではないと」「残念ながらそっち系の楽しい話ではないですね🤣


opalrb.comより

mrubyをWebAssemblyで動かす(翻訳)

⚓標準のCSVライブラリはどうしてコケるのか(Ruby Weeklyより)

# 同記事より
require 'csv'
require 'pp'

begin
  CSV.parse( %{1, "2"} )
rescue CSV::MalformedCSVError => ex
  pp ex
end
# => #<CSV::MalformedCSVError: Illegal quoting in line 1.>

begin
  CSV.parse( %{"3" , 4} )
rescue CSV::MalformedCSVError => ex
  pp ex
end
# => #<CSV::MalformedCSVError: Unclosed quoted field on line 1.>

pp CSV.parse( %{"","",,} )
# => ["", "", nil, nil]

つっつきボイス:「CSVは今はそこそこマシになった気がするけど」「どっちかというとCSVの仕様をどうにかせいというボヤキ記事みたいです」「CSVって結局『オレがCSVと言っているものがCSVだっ!』っていう程度のものだし😎」「マイクロソフトのCSVはそれなりに普及してますけどね」「どんなCSVが好き?と聞いてみるテスト」「…CSVライブラリで読み込めるのがいいCSV☺

「クォーテーションやカンマがセルで使われてない場合に省略できるかどうかとか、ほんとどうでもいいルールがいろいろあるし」「…一応RFCに仕様あるんですけど誰も使ってないし」「ひど😭」「W3CRFCとExcelどっちか選ぶとしたらExcelですよね☺

「私はタブ区切りテキスト(TSV)の方がエスケープ楽だし安心感あるんですけど、皆さんCSVを使うのはなぜなんだろう?と思って」「よくはわからないけど、結局業務システムでCSVが使われることが多いからですかね〜: TSVの方が使いやすいけど」「そうそう」

「一応CSVはCharacter Separatedなので、カテゴリ的にはTSVもCSVに一応含まれます」「へー!😲」「完璧に後付けですけどね🤣

「TSVならExcelにそのまま貼り付けられるという絶大なメリットあるのにー😤」「Webのフォームでも『Excelをここからここまでコピーしてそのまま貼り付けてください』っていうのよく使う😋

参考: library csv (Ruby 2.5.0)

⚓Railsアプリの基本的なセキュリティの注意(Ruby Weeklyより)


つっつきボイス:「割と普通の内容かなと思いましたが」「yamlに式展開↓、普通やらないっすよね〜😅」「これやっちゃうとhtml_safeしないと出せないから、i18nというよりはhtml_safeヤメレという話」

# 同記事より
# en.yml
en:
  hello: "Welcome <strong>%{user_name}</strong>!"

RailsビューのHTMLエスケープは#link_toなどのヘルパーメソッドで解除されることがある

「そして解決方法はというと、なになに、yamlのキー名を_htmlで終わらせると自動的にエスケープしてくれる↓…だと…?」「やべーこれ知らなかった〜!」「しかもRailsガイドにも載っている…だと?」「これは知らなかった…が、i18nに_htmlって書かせたくない感が残るw まあいいけど」

# en.yml
en:
  hello_html: "Welcome <strong>%{user_name}</strong>!"

「こういうのもそもそも書かないだろうし↓、記事にあるようにpermitリストを通してからやるべき」「ところでconstantizeは文字列からクラスをnewできるリフレクション向けの機能」「…機能限定版のeval」「ですです」「brakemanで絶対怒られるヤツ」「brakemanは静的セキュリティチェックの定番ツールで、こういう情けないレベルの穴はだいたい見つけてくれる優秀なヤツです💪

# 同記事より
class FooForm; end
class BarForm; end

form_klass = "#{params[:kind].camelize}Form".constantize # ダメ絶対
form_klass.new.submit(params)

参考: Ruby on Rails 5.2 / String#constantize — DevDocs

参考: module function Kernel.#eval (Ruby 2.5.0)

「これ↓はidを取ってるつもりがarrayが来てしまうヤツ」「1つ消すつもりが複数消してしまったりすると」「こういうのを防ぐためにStrong Parametersが導入された」「Strong Parameters使ってれば普通は大丈夫💪: Ransackとか使うときは要注意かな」

# 同記事より
  # POST /delete_user?id=xxx

  def can_delete?(user_id)
    other_user = User.find_by(id: user_id)
    current_user.can_delete?(other_user)
  end

  user_id = params[:id]

  if can_delete?(user_id)
    User.where(id: user_id).update(deleted: true)
  end

「最後の注意点↓も基本的なものなので当然知っとくべき」「ローカルsshログのリモートコード実行例もちょっと気になりました」「ああこれは割と古典的な手口ですね: 外から持ってきたファイルをinclude("$file");するとか、まずコードレビューとおらないっしょ😤

⚓marginalia: ActiveRecordのSQLクエリにコメントを付けられるgem(Ruby Weeklyより)

# 同リポジトリより
Account Load (0.3ms)  SELECT `accounts`.* FROM `accounts` 
WHERE `accounts`.`queenbee_id` = 1234567890 
LIMIT 1 
/*application:BCX,controller:project_imports,action:show*/

Basecampのgemです。以前翻訳したこの記事↓でも軽く紹介されていました。

Rails開発者のためのPostgreSQLの便利技(翻訳)


つっつきボイス:「あーなるほど!どこからSQLが発行されたかを追えるのか!」「しかもそれをpt-query-digestで追えるとは、これはかなり( ・∀・)イイ!!」「お、また知らないものが💦」「pt-query-digestは、例のPercona Toolkitのひとつで、高速化とかチューニングによく使います」「ちなみに昔はMaatkitという名前でした」

「pt-query-digestは、MySQLから出力されたでかいログを食わせると、期間内にどんなクエリがどのぐらいあるかを出してくれます」「並のツールだと1件ずつのスロークエリは調べられても、よく似たたくさんのクエリが少しずつ遅いみたいなN+1的なクエリだと調べきれないんですが、pt-query-digestはそういう頻度の高い類似クエリを調べることができる」「おぉ~😲」「まあ今だとnewrelicとか使えばわかるんですが」

「marginaliaを使うとそのpt-query-digestにかけられる形式のコメントが出るってことなんでしょうね」「marginalia、よく見たらBasecamp(Railsの作者DHHのいる会社)のgemじゃん!」「まぁBasecampならnewrelicとか死んでも使わなさそうではある🤣」「🤣」「🤣

⚓localhost: 自己署名ルート証明書をユーザーごとに生成する開発用gem

# 同リポジトリより
require 'socket'
require 'thread'

require 'localhost/authority'

# Get the self-signed authority for localhost:
authority = Localhost::Authority.fetch

ready = Thread::Queue.new

# Start a server thread:
server_thread = Thread.new do
    server = OpenSSL::SSL::SSLServer.new(TCPServer.new("localhost", 4050), authority.server_context)

    server.listen

    ready << true

    peer = server.accept

    peer.puts "Hello World!"
    peer.flush

    peer.close
end

ready.pop

client = OpenSSL::SSL::SSLSocket.new(TCPSocket.new("localhost", 4050), authority.client_context)

# Initialize SSL connection:
client.connect

# Read the encrypted message:
puts client.read(12)

client.close
server_thread.join

つっつきボイス:「localhostっていうgem名が凄すぎ」「ヤメレw: require "localhost"とか書きたくないし😆」「gem 'localhost'で入るってことはネームスペース空いてたのね☺

「このgemは自己署名証明書をローカルで開発用に立てられるということですけど、ときどき必要になる感じでしょうか?」「ですね: SSL/TLSがないと使えない機能があるとか」「Rackで使えるSSLとか使う手もありますが」「ローカルのVirtualHostが証明書を発行できればそれでいいのにと思ったりはしますが☺」「自分の手元のターミナルでopen sshして手元のNginxあたりに設定すればいいんじゃね?という気も」「今はDockerがあるからそれで十分ですしね: でも全部Rubyでやりたい人たちがいるんですよきっと😆

⚓google_sign_in: GoogleaアカウントでRailsにサインイン(GitHub Trendingより)

# 同記事より
# app/controllers/logins_controller.rb
class LoginsController < ApplicationController
  def new
  end

  def create
    if user = authenticate_with_google
      cookies.signed[:user_id] = user.id
      redirect_to user
    else
      redirect_to new_session_url, alert: 'authentication_failed'
    end
  end

  private
    def authenticate_with_google
      if flash[:google_sign_in_token].present?
        User.find_by google_id: GoogleSignIn::Identity.new(flash[:google_sign_in_token]).user_id
      end
    end
end

非常に新しいですがissueは2017年だったりするので社内で使ってたのかも。


つっつきボイス:「これもBasecampですね」「OmniAuthは絶対使いたくないとかあったりして😆」「BasecampってDevise使ってるんだろうか…?」「OmniAuthってそもそも抽象度が高すぎるんですよね〜: OmniAuthでOAuth2認証すると、プロバイダごとにデータ構造なんかが全然違ってたりしますし😢

⚓RailsのGraphQLでページネーションする(RubyFlowより)


つっつきボイス:「少し前のウォッチでもページネーションの話しましたが、Google APIで何か実装したことってあります?」「まだです〜」「あの手のAPIはだいたい50件ずつぐらいしか取れないんで何らかの形でページネーションが必要になってくる: 何の工夫もしなくてもページネーションはできちゃうんですが、ちゃんとやるなら次のページに進んでいる間にデータの件数が増えたりする場合にも対応しないといけなくなる」「でGoogleのはさすがによくできていて、最初のページを取ってきたときに、そういう点にも配慮したnext page urlも一緒に返してくれるのがありがたい」「ページネーションの整合性が取れるんですね😃」「で、GraphQLのページネーションでもたぶんそういうところをケアしないといけないんでしょうね」

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

⚓StimulusJSとActionCableにSidekiqを少々(RubyFlowより)

import cable from 'actioncable';

let consumer;

export default function (...args) {
  if (!consumer) {
    consumer = cable.createConsumer();
  }

  return consumer.subscriptions.create(...args);
}

つっつきボイス:「これは普通の非同期記事かな」「StimulusJSって何だっけ」「これもBasecampです」「そうだった: Turbolinksと相性がいいとか何とか」


stimulusjs.orgより

⚓RubyとEmacsのヒント集(RubyFlowより)


つっつきボイス:「VimmerからEmacsへの風評被害は最近落ち着いたのかなw」「🤣」「🤣」「Vimmerはネタに走りすぎ感」

(突然のエディター大喜利による音声輻輳)「Vim」「Emacs」「開発環境?それともターミナルでファイルを変更するとき?」「JetBrains IDE」「Sublime」「Atom」「VSCode」「VSCodeは最近快進撃っぽい?」「TypeScriptのお膝元だからかな」

「そうそう、EmacsでVimキーバインドをエミュレートするその名もEvil↓っていうのがありますヨ😈」「これはw」「凶悪」


emacswiki.orgより

⚓値ベースのページネーション


同記事より


つっつきボイス:「これは何がしたいのかな?🤔」「あー、OFFSETでページネーションするとDBの内部処理が発生して遅くなるので、インデックス化されているカラムを使ってWHEREで絞り込もうよってことか: めちゃくちゃでかいテーブルだとOFFSETの速度低下が顕著に出ますね」「それってRailsに限った話じゃなくね?って思うけど😆」「そういえばfind_eachってこんな感じの動きしますよね?」「そうそう、idベースでやってるし:activerecord-importとかもそんなのやってたかも」

# 同記事より
# ./models/application_record.rb

class ApplicationRecord < ActiveRecord::Base
  self.abstract_class = true

  class << self
    def start_at(id: 1, timestamp: Time.now, column: :created_at, limit: 10)
      params = parsed_params(id, timestamp, column, limit)
      where("#{table_name}.#{params[:column_name]} < :value OR\
               (#{table_name}.#{params[:column_name]} = :value AND id > :id)",
            value: params[:timestamp], id: params[:id])
        .order(column => :desc)
        .limit(params[:limit])
    rescue ArgumentError
      order(created_at: :desc).first(params[:limit])
    end
...

参考: Ruby on Rails 5.2 / ActiveRecord::Batches#find_each — DevDocs

⚓RSpecは何を参考にするか


つっつきボイス:「RSpecは、本を読むよりも誰かが書いたRSpecを参考にできると嬉しいと思う」「その誰かは誰にすればいいでしょうか?🤔」「みんながまともと呼んでいるプロジェクト🤣」「🤣」「同僚がいない場合はどうしましょう?(ひとりでやってるの〜😭)」「それはつらそう😢: オープンソース系のプロジェクトを参考にすることになるけど、いきなりレベルの高すぎるRSpecを見ても難しすぎてよくわからないという🤣」「素振りに手頃なテストコードを知りたい気持ちです🏏」「そうそう、このぐらいユルくてもまずは書いてくれればいいよみたいなの😊: それこそこういう本を参考にするのがいいかな」

「ちなみに自分はletキライ派」「なぬっ、ワイはletしか使わない派w✨」「こうやって戦争が始まるのか🤣」「shared_examplesは悪みたいなのは最近広まったかな?」「share_examplesも使ってるですよこれがw✨: 使わないとコピペアレルギーが出る〜」「share_examplesは3つまでは許せるけど〜」「てな感じになるわけですよ😆」「これが宗教戦争というヤツですねおっかさん😊

「…テストあんまり真面目に書いてないし🤣」「いっそRSpecをやめるというのは?🤣みんなminitestでassertion使えば宗教が起きない、きっと」「テストコードのリファクタリングとか始めると宗教になりそうだな〜」「メソッドのリファクタリングならいいんですけど、凝りまくった器用なマッチャーとか書いて『これがテストのリファクタリングだ』とドヤ顔で言われても〜🤣」「🤣」「🤣」「英語っぽい自然なテストが書きたいんじゃないんだけどな〜🤣

「まあとにかく、RSpecはこういう本を参考にして、あんまりスゴイのを書くようにしなくても十分じゃないのって個人的には思うわけですよ」「…きっとassertだけでいいと思うし: イコールも== trueみたいなのでいいと思ってるし」「🤣」「自分もRSpecではほとんどeqでやってますね〜」「🤣」「RSpecのテストコードがちゃんと動いているかどうかが気になってくると本末転倒感ある🧐

Rails tips: RSpecテストの高速化/リファクタリングに役立つ4つの手法(翻訳)

⚓Ruby trunkより

まだスパムissueが続いているようです。

⚓提案: C APIの公式なドキュメントを作ろう


つっつきボイス:「一応あるはあった気がするけど公式ではない?」「↓これがそれに近いらしいですが、足りないものもあるようです」

参考: The Ruby C API

「確かにRubyのGILやFiberとか、モジュール読み込みのancestors周りとかも含めて公式のドキュメントがあるとありがたいという気持ちはワカル: Rubyの進化にどこまでドキュメントが追いつけるかというのはあるけど」「…わかっている人には要らないのかもという気はする」「そこまで潜らないといけないことは普段はそうそうありませんけどね☺

⚓keyword_initを指定せずにStruct使うと動作がおかしい => 却下

Info = Struct.new(:name, :country)  # キーワード引数を有効にしていない
c = Info.new(name: "myname", country: "Japan") # キーワード引数でインスタンス化される

Info = Struct.new(:name, :country, keyword_init: true) # これは動く
c = Info.new(name: "myname", country: "Japan")
p c #=> #<struct Info name="myname", country="Japan">

つっつきボイス:「自分でも動かしたらそうなりました」「ホントだ😲」「ハッシュとして最初の引数に全部入っちゃうと」「キーワード引数じゃない場合って、引数足りなくても動くんでしたっけ?」

↓やってみたら、キーワード引数であってもなくても引数足りない場合に動きました。

[1] pry(main)> Info = Struct.new(:name, :country)
=> Info
[2] pry(main)> a = Info.new(name: "myname")
=> #<struct Info name={:name=>"myname"}, country=nil>
[3] Info = Struct.new(:name, :country, keyword_init: true)
=> Info(keyword_init: true)
[4] pry(main)> a = Info.new(name: "myname")
=> #<struct Info name="myname", country=nil>

「自分はStruct.newってあまり使わないけど、kazzさんはValue Objectで割と使ってましたっけ?」「最近はそんなに頻繁には😅: Struct意外に難しくて、いろいろ足していくうちに普通にクラス書く方がよくね?っていう感じになっていったり」「確かに、クラス作るのが面倒だからと思ってStructでやろうとすると意外に思ったとおり行かなかったりしますね☺

⚓Ruby

⚓LinuxデスクトップアプリをRubyでがっつり作った(Ruby Weeklyより)


同記事より

やっぱりGTK+でやってます。かなり長い記事ですが、こんなに詳しく解説しているのは珍しいと思って。


つっつきボイス:「RubyのGUIアプリって、Railsが流行る前には結構ありましたね: 自分も初めて触ったRubyはGUIだったし❤」「この記事のは結構ガッツリ作ってる方かしら?」「このぐらいのは普通にありましたね」「…大抵の言語にはGTKのバインディングあるし、昔は流行ってたけど最近はあんまり流行ってない😆」「Ruby何だかんだで年いってる言語だし🎅


gtk.orgより

参考: GTK+ - Wikipedia

⚓Rubyをテーマにした論文リスト(Ruby Weeklyより)


つっつきボイス:「思ったより論文あるなと思って」「結構ありますよ」「知ってる著者はあっても、知らないカンファレンスがいっぱい😅

⚓ProcがCallableより遅い件について(Hacklinesより)

# 同記事より
Comparison:
     callable no arg: 10095848.2 i/s
   callable with arg:  9777103.9 i/s - same-ish: difference falls within error
     callable 3 args:  9460308.0 i/s - same-ish: difference falls within error
callable splat args (0):  6773190.5 i/s - 1.49x  slower
         proc no arg:  6747397.4 i/s - 1.50x  slower
       proc with arg:  6663572.5 i/s - 1.52x  slower
         proc 3 args:  6454715.5 i/s - 1.56x  slower
callable splat args (1):  5099903.4 i/s - 1.98x  slower
 proc splat args (0):  5028088.6 i/s - 2.01x  slower
callable splat args (3):  4880320.0 i/s - 2.07x  slower
 proc splat args (1):  4091623.1 i/s - 2.47x  slower
 proc splat args (3):  4005997.8 i/s - 2.52x  slower

つっつきボイス:「Callableってこの場合?」「↓PORO(素のRubyオブジェクト)のcallメソッドを呼ぶことみたいです」「callがいくつもあると早口言葉みたい😆」「トートロジー😆」「Procの方が遅い?」「Procが遅いのはもうしょうがない☺: その場で解決しないといけないし」

calling a PORO’s call method — that is, a callable object

⚓Rubyコミュニティのおすすめリソース(RubyFlowより)


同記事より

⚓strings-ansi: 文字列のANSIエスケープを検出/サニタイズするgem(Ruby Weeklyより)

# 同リポジトリより
Strings::ANSI.sanitize("\e[0;33;49mHello\e[0m")
# => Hello

つっつきボイス:「ANSIエスケープって何だっけと思って」「そうそう、\e[30m \e[40mみたいにターミナルの色を変えたりするときに使いますね」「そしてよくバグる😆」「自分のターミナルのPowerlineでもよく出力がおかしくなったりするし😭: screenrcとかいじるのは楽しいけどねっ☺」「自分もついやっちゃう😆

参考: ANSIエスケープシーケンス チートシート - Qiita


github.com/powerline/powerlineより

⚓Sider社がRuboCopのスポンサーに(Hacklinesより)


つっつきボイス:「Siderは日本のCIの会社です」「そういえばRubyKaigi 2017でもRuboCopの日本人コントリビュータが発表してましたね」「RuboCop、もうちょっと人情味があってもいい気が😭: デフォルト設定だと厳しすぎっ」「慣れると『へーそんな書き方あるんだー』『勉強になったなぁー』って心境になれる☺


sider.reviewより

その他Ruby



つっつきボイス:「確かIIJのルーターもmrubyで書かれてるって聞いた気がする」

参考: 軽量Rubyへの取り組み | IIJの技術 | インターネットイニシアティブ(IIJ)



つっつきボイス:「ささいなようだけど、エラーページをこのぐらいいい感じに作っておくだけで和みますよね☺」「和んだ〜」「この間札幌大学のサイトが落ちたときのエラーページにクラーク先生の御姿があってこれも和んだ☺」「Railsデフォルトの赤いアレよりは遊び心あるのが( ・∀・)イイ!!」「TechRachoもそういうエラーページ出せばいいのに😆」「WordPressが安定してるので出番がなかなかないかなー😆

参考: ウィリアム・スミス・クラーク - Wikipedia

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

⚓tcpdump101.com: パケットキャプチャ設定支援(WebOps Weeklyより)


つっつきボイス:「tcpdumpのコマンドビルダーだそうです」「101ってどういう意味だろう?🤔」「Wireshark(旧Ethereal)もGUIよくできてるしそっちでいいかなー☺

参考: Wireshark - Wikipedia


wireshark.orgより

⚓マイクロソフトがGoogleアカウントでシングルサインオンに踏み切る

参考: マイクロソフト、GoogleアカウントでWindowsやOffice 365へのシングルサインオンを可能に。Azure Active Directoryの新機能をプレビュー公開 - Publickey


つっつきボイス:「ほほー?」「これはいい文明?」「MSアカウントはマジ残念なこと多すぎるし😆なぜか使うたびにサインインを求められるのは気のせい?」「…結構気難しいんですよねMSアカウント🧐: 日によってEdgeでないと入れなかったりChrome Canaryじゃないと入れなかったり、でだいたい3つめで入れる🤣」「難しいってそれってアカウントとしてどうよ🤣」「レインボーカーソルのくるくる具合がおかしい🤪ときなんか、強制リロードしないとログインできなかったり」「ちなみにツイートで言ってるのはAzure ADの方ですね」「あそっちか」

参考: Azure Active Directory (Azure AD) とは | Microsoft Docs

⚓vegeta: HTTP/2に対応した負荷ツール(WebOps Weeklyより)


つっつきボイス:「そのまんまベジータ画像を堂々と使っちゃってるんで記事に引用できないー😭」「ちょさっけん的にアウトじゃん🤣」「ど真ん中でアウト🎯」「しかもターゲットがgokuだし」「ベジータから悟空に攻撃するんだよやっぱ😆

# 同記事より
GET http://goku:9090/path/to/dragon?item=ball
GET http://user:password@goku:9090/path/to
HEAD http://goku:9090/path/to/success

「しかしこれ、欲しいオプションはひととおりあるし、負荷テストツールとしては結構よさそうだし😍」「あとはマルチスレッド性能とか、どのぐらい凄いトラフィックを作れるかかな」「★9000近いし人気は抜群ですね」

「この種の負荷ツールは、単体でどれだけエグいトラフィックを作れるかというのも重要な指標」「やっぱ戦闘力の高さが大事なんですね😆」「エグい負荷を出せないと負荷測定にならないので」「これはRuby?」「えっと、Go言語でした」

⚓その他クラウド

⚓SQL

⚓PostgreSQLのログ先行書き込み(WAL)が肥大化する理由(Postgres Weeklyより)


つっつきボイス:「WAL: write ahead log」

⚓litetree: ブランチできるSQLite(DB Weeklyより)


同リポジトリより

PRAGMA branch=<name>.<commit>

つっつきボイス:「へぇえー?SQLiteのスナップショットを取ってブランチできる?」「ちょっと珍しいかなと思って」「最近のAWS Auroraにあるスナップショット機能みたい」「お、既にそういうのあったんですね💦

参考: Amazon Aurora(MySQL、PostgreSQL 互換のリレーショナルデータベース)|AWS

⚓JavaScript

NodeWeeklyというニュースサイトがあることに今頃気づきました。

⚓Babel 7がリリース(JavaScript Weeklyより)

主なbreaking changes:

  • Node.js 5以下のサポート打ち切り
  • @babel名前空間の導入(babel-core@babel/coreに変わる)
  • 年を含むpresetを廃止(preset-es2015など)
  • stage presetを廃止

変更量めちゃめちゃ多いです。

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

⚓手描きスケッチをHTMLコード化


つっつきボイス:「SketchといったらWeb屋はふつうこっち↓を指すだろうって🤣」「名前はともかく機能的にはよさそうなんですけどね」「MSが戦い挑んでる🏹

参考: 現役WebデザイナーがデザインツールをSketch一択にした理由 – Cntlog

⚓セキュリティ/ハッキング記事サイト

⚓Chromeのlazyload属性

<iframe src="ads.html" lazyload="on"></iframe>

つっつきボイス:「そうそうこれ最近出てましたね: lazyload="on"付けるだけでレイジーになってくれる」「へぇえー!😳」「まだChromeだけか…😢

参考: Chromeの新機能がすごい便利!imgやiframeにlazyload属性を加えるだけでLazyLoad対応に | コリス

⚓言語よろずの間

⚓TIOBEランキングでPythonがC++を抜く

参考: 「Python」が初のトップ3入り、「Julia」上昇--TIOBE - ZDNet Japan

⚓bat: シンタックスハイライトつきcatGitHub Trendingより)



同リポジトリより

Rustで書かれています。exaというRust版のlsもあるそうです。


つっつきボイス:「catからbatへ」「『翼の生えたネコ』ってあるから明らかに狙ってるし」「にゃ~ん(ΦωΦ)」「UnixコマンドをRustで書き換えるの最近よく見かけますね☺

私もripgrep(やはりRust製)を最近愛用してます😍

⚓rockstar: 100% Rubyで書かれた動的型言語(GitHub Trendingより)

poetic licenseを目指してるとか、何だか相当なクセ球の予感。新しいのに★4000超えてます。

参考: poetic licenseの意味・使い方 - 英和辞典 Weblio辞書

Modulus takes Number and Divisor
While Number is as high as Divisor
Put Number minus Divisor into Number
    (blank line ending While block)
Give back Number
    (blank line ending function declaration)
Limit is 100
Counter is 0
Fizz is 3
Buzz is 5
Until Counter is Limit
Build Counter up
If Modulus taking Counter, Fizz is 0 and Modulus taking Counter, Buzz is 0
Say "FizzBuzz!"
Continue
    (blank line ending 'If' Block)
If Modulus taking Counter and Fizz is 0
Say "Fizz!"
Continue
    (blank line ending 'If' Block)  
If Modulus taking Counter and Buzz is 0
Say "Buzz!"
Continue
    (blank line ending 'If' Block)
Say Counter
    (EOL ending Until block)

つっつきボイス:「自然言語っぽいプログラミング言語を目指してる感」「コメントはご法度だそうです😆」「poetic licenseって何だろうと思ったら『詩的許容』というやつでした」「どことなくVBっぽい匂いもする」

⚓その他

⚓英語辞書Chrome拡張


つっつきボイス:「これスピード速くてすごくいいんですが、手持ちの辞書はほとんど読み込めないので私にはまだ使いみちがない😭

⚓Electronで動くWindows 95


つっつきボイス:「やっぱりレガシーゲーム用?」「DOOMとかね」

⚓番外

⚓一発ネタ


⚓戦時中生まれ

Rob Pikeより15歳も年上でした😳


つっつきボイス:「『闘うプログラマー』好きでしたよね?」「…好きです❤、目の前の本棚のどこかにあったはず」「後で探そうっと☺


「今週は以上です」「お疲れさまでしたー!😊

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

週刊Railsウォッチ(20180827)Ruby Prize 2018募集開始、Interactor gemとReader Object、書籍『Real World HTTP』、Basecampのヒルチャート機能ほか

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

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

Rails公式ニュース

Ruby Weekly

RubyFlow

160928_1638_XvIP4h

Hacklines

Hacklines

WebOps Weekly

webops_weekly_banner

Postgres Weekly

postgres_weekly_banner

DB Weekly

db_weekly_banner

Frontend Weekly

frontendweekly_banner_captured

JavaScript Weekly

javascriptweekly_logo_captured

Golang Weekly

golangweekly_logo_captured


Viewing all articles
Browse latest Browse all 1759

Trending Articles