週刊Railsウォッチ(20190722-1/2前編)Rails 6エラー画面の改良点、Dateを四捨五入できるtime_calc、Rackミドルウェアのデザインパターンほか

こんにちは、hachi8833です。7年近く肌身離さず使い続けたMacbook Pro 2013 LateのSSDのメインパーティションが、先週金曜日に天に召されました😇

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

お知らせ2件

「出張Railsウォッチ in 銀座Rails」のお知らせ

morimorihogeからのお知らせ:

7/24開催予定の銀座Rails#11 @リンクアンドモチベーションにRailsウォッチが出張予定です。
都合により後半からの参加予定ですが、懇親会枠には間に合いますのでよろしければお声かけください。

週刊Railsウォッチ「公開つっつき会」第13回のお知らせ(無料)

1年目を越えた第13回目公開つっつき会は、8月1日(木)19:30〜にBPS会議スペースにて開催されます。皆さまのお気軽なご参加をお待ちしております🙇。

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

database_exists?をadapterに追加

# activerecord/lib/active_record/connection_adapters/abstract_adapter.rb#L267
+     # Does the database for this adapter exist?
+     def self.database_exists?(config)
+       raise NotImplementedError
+     end
# activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb#L45
+     def self.database_exists?(config)
+       !!ActiveRecord::Base.mysql2_connection(config)
+     rescue ActiveRecord::NoDatabaseError
+       false
+     end

あえてpublicメソッドにしたそうです。


つっつきボイス:「まああれば使うかな☺️」「MySQL向けかな?」「abstract_adapterに足されてるから全部のadapterに足されますね」「あ、そうか😅」

「これもマルチプルDB対応でしょうか?」「どうだろう🤔、シングルDBなら最初に接続したときに落ちていればすぐわかるけど、マルチDBだと途中でconnection_adaptersでアクセスしているときに落ちる可能性もあるから、もしかするとこういうので対応するようにしたのかなあ」「とりあえずあっていい機能だと思いますね😋」「マルチDB環境で、複数の異なる種類のDBに接続するケースの対応を意図しているのかも?: MySQLAdapterとPostgresqlAdapterをそれぞれ接続した場合に、ちゃんとそれぞれのdatabase_exists?実装で動いて欲しい、的な」※捕捉参照

SQLiteは接続時にデータベースが存在しないと、こっそりデータベースを作成する。この振る舞いはアダプタ間で異なるので他の問題を引き起こす。このコミットはdatabase_exists?メソッドを追加して、データベースを作成せずにデータベースをチェックできるようにした。問題解決の第一歩としてこれを行った。
同コミットより大意

morimorihoge捕捉(2019/07/23 02:25):
その後Twitterにて@kamipoさんより本件は複数DB対応とは関係ない話であるとのご指摘いただきました。
指摘頂いた内容を追いかけ直してみると、#36383db:prepareの挙動がPostgreSQL AdapterとSQLite3 Adapterで異なる点について解説されていました。

背景として、db:prepareは元々db:migrateが実行され、もしDBがなければAR::NoDatabaseErrorがraiseされることを経由することで、本来DBがなければdb:setupが実行されることを期待されていました。
しかし、SQLite3 AdapterではDBがない状態でもAR::NoDatabaseErrorをraiseせず、暗黙的にDBファイルの作成を行ってしまうことから、db:setupで実行されるはずのschemaとseedsの読み込みが行われていませんでした。

このあたりをdatabase_exists?メソッドに処理を統合しつつ、SQLite3 Adapterでのチェックもファイル存在チェックからメソッドによるチェックに統合することで、SQLite3環境での挙動が本来期待するものになっていなかったのを修正するという流れであった、と思われます。

# kamipoさん、ご指摘ありがとうございました!

コネクション間で引用符付きカラムとテーブル名のキャッシュを共有

# activerecord/lib/active_record/connection_adapters/mysql/quoting.rb#L5
    module MySQL
      module Quoting # :nodoc:
        def quote_column_name(name)
-         @quoted_column_names[name] ||= "`#{super.gsub('`', '``')}`"
+         self.class.quoted_column_names[name] ||= "`#{super.gsub('`', '``')}`"
        end

        def quote_table_name(name)
-         @quoted_table_names[name] ||= super.gsub(".", "`.`").freeze
+         self.class.quoted_table_names[name] ||= super.gsub(".", "`.`").freeze
        end
# activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb#L
        def quote_table_name(name) # :nodoc:
-         @quoted_table_names[name] ||= Utils.extract_schema_qualified_name(name.to_s).quoted.freeze
+         self.class.quoted_table_names[name] ||= Utils.extract_schema_qualified_name(name.to_s).quoted.freeze
        end
...
        def quote_column_name(name) # :nodoc:
-         @quoted_column_names[name] ||= PG::Connection.quote_ident(super).freeze
+         self.class.quoted_column_names[name] ||= PG::Connection.quote_ident(super).freeze
        end

つっつきボイス:「quote_column_nameなんてあるんですね」「ありますあります、MySQLだとバッククォート`でやります🧐」「バッククォートで?!」「ぽすぐれはダブルクォートだったかな、データベースによってこのあたりが微妙に違っているという😆」「PostgreSQLはUtils.extract_schema_qualified_nameというメソッドが生えてる☺️」

「この改修は、こういうquoted columnsをコネクション間で共有する方がいいよみたいなヤツなのかな🤔」「名前をキャッシュするということ?」「定義は基本的に変わらないものですし☺️」「だからメモ化しようぜと」「コネクションアダプタは複数になってもここは変わらないでしょと」「SQL Standard的にはダブルクォートがカラムのクオート文字↓」

参考: sql - What is the difference between single quotes and double quotes in PostgreSQL? - Stack Overflow

コネクション共有時のクエリキャッシュを修正

# activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb#L428
      def connection
-       @thread_cached_conns[connection_cache_key(@lock_thread || Thread.current)] ||= checkout
+       @thread_cached_conns[connection_cache_key(current_thread)] ||= checkout
      end

      # Returns true if there is an open connection being used for the current thread.
      #
      # This method only works for connections that have been obtained through
      # #connection or #with_connection methods. Connections obtained through
      # #checkout will not be detected by #active_connection?
      def active_connection?
-       @thread_cached_conns[connection_cache_key(Thread.current)]
+       @thread_cached_conns[connection_cache_key(current_thread)]
      end
...
+       def current_thread
+         @lock_thread || Thread.current
+       end
# activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb#L35
        def enable_query_cache!
-         @query_cache_enabled[connection_cache_key(Thread.current)] = true
+         @query_cache_enabled[connection_cache_key(current_thread)] = true
          connection.enable_query_cache! if active_connection?
        end

        def disable_query_cache!
-         @query_cache_enabled.delete connection_cache_key(Thread.current)
+         @query_cache_enabled.delete connection_cache_key(current_thread)
          connection.disable_query_cache! if active_connection?
        end

        def query_cache_enabled
-         @query_cache_enabled[connection_cache_key(Thread.current)]
+         @query_cache_enabled[connection_cache_key(current_thread)]
        end
      end

スレッドをまたがる共有コネクションが有効な場合にクエリキャッシュが正しいコネクションで利くようにした。
同PRより大意


つっつきボイス:「これもコネクションの共有周りか」「スレッドになってる」「current_threadにまとめたと」「これで詰まったりしないのかな?」「クエリキャッシュだし、そんなに遅くなったりはしなさそうだけど🤔」

relation_exists?を修正

# activerecord/lib/active_record/relation/finder_methods.rb#L353
      def construct_relation_for_exists(conditions)
        conditions = sanitize_forbidden_attributes(conditions)

        if distinct_value && offset_value
-         relation = limit(1)
+         relation = except(:order).limit(1)
        else
          relation = except(:select, :distinct, :order)._select!(ONE_AS_ONE).limit!(1)
        end
        case conditions
        when Array, Hash
          relation.where!(conditions) unless conditions.empty?
        else
          relation.where!(primary_key => conditions) unless conditions == :none
        end
        relation
      end

つっつきボイス:「JOINしたテーブルでdistinctoffsetorderを指定するとrelation.exists?でエラーになったと」「exceptというのは?」「:orderのスコープをあえてexceptで外していますね」「PostgreSQLでorderがつくとエラーになってたのか」「えぇ〜」「ぽすぐれだと『使ってないカラムがあるよ』みたいなエラーがよく起きるからそれかも?」「あ〜」「このややこしい組み合わせのときに起きるエラーっぽい」

issue: ActionController::Baseのdeprecation warningが紛らわしい

DEPRECATION WARNING: Initialization autoloaded the constants ActionText::ContentHelper and ActionText::TagHelper.

Being able to do this is deprecated. Autoloading during initialization is going
to be an error condition in future versions of Rails.

Reloading does not reboot the application, and therefore code executed during
initialization does not run again. So, if you reload ActionText::ContentHelper, for example,
the expected changes won't be reflected in that stale Module object.

These autoloaded constants have been unloaded.

Please, check the "Autoloading and Reloading Constants" guide for solutions.
 (called from <main> at /Users/
USER/Projects/rails-app-test/config/environment.rb:5)

つっつきボイス:「こちらはPRじゃなくてissueです」「出なくていいwarningが出るのね」「warningが鳴ったら気にするけど、あまりに多かったりすると見てられなくなったりしますし😆」

Rails

Rails 6のエラー表示を目にして思ったこと(Ruby Weeklyより)


同記事より


つっつきボイス:「Rails 6のエラー画面の嬉しい点と残念な点と見苦しい点はこれとこれ、みたいな記事です😆」「Rails 6のエラー画面って地道に改良されてますね😋」「エラーがハイライトされるようになって、トレースもフィルタできるようになって、コンテキスト情報もリッチになって🎂」「でもオートサジェスチョンがうまくいかないことがあるとか😆」「あとこれ↓が出てもうれしくないとか😆」「Railsあるある😆」「Railsやってればどこでも出てきますし😆」


同記事より

直接関係ありませんが、記事タイトルの「The Good, The Bad, The Ugly」といえばエンニオ・モリコーネ作曲の一度聴いたら忘れられないこのテーマソング。

参考: 続・夕陽のガンマン - Wikipedia

その名はRack::CORSRuby Weeklyより)

# 同記事より
config.middleware.insert_before 0, Rack::Cors do
  allow do
    origins 'example.com', 'localhost:3000'
    resource '/publicStuff/*', headers: :any, methods: [:get, :post]
    resource '/myStuff/*', headers: :any, methods: :any
  end
end

つっつきボイス:「Rack::CORS、取り上げたことがありそうでなかったので」「Cross-Origin Resource Sharing」「割と細かく設定できそうなgem😋」

参考: オリジン間リソース共有 (CORS) - HTTP | MDN

Rack::CORSは★も2600とかなり多いですけど、どんなときに使うんでしょう?」「APIサーバーとかやるときはこういうのをやらないと動きません」「あ〜」「クロスドメインのリクエストをJavaScriptで投げようとすると、そのままでは制約に引っかかりますね」

「Chromeだったかな、リクエストはかろうじて渡せたけど中身が空っぽになってたときはえぇ〜?!って気持ちになりましたし😆」「😆」「むしろ動かずにエラー表示して欲しいんだけど〜😅」「CORSの話はちょいちょい出てきますね☺️」

参考: Ruby on Rails 5 アプリにあとから API 機能(JWT, CORS 対応)を追加する - 無印吉澤

Rails 6のActive Jobリトライ/discardフック(Ruby Weeklyより)

# 同記事より
class Container::DeleteJob < ActiveJob::Base
  retry_on Timeout::Error, wait: 2.seconds, attempts: 3

  def perform(container_id)
    Container::DeleteService(container_id).process

    # Will raise Timeout::Error when the remote API is not responding.
  end
end

つっつきボイス:「Rails 6の新機能なんですけど、おそらく今まで取り上げてなかった😅」「Active Jobのretrydiscardか」「へぇ〜!」「やったことあった気もするけど?」「とりあえず見当たりませんでした…」

「必要ですね☺️」「必要」「discardとか特に欲しいし🥰」「ね😆」「ゴミファイルが無限にたまらないようにしたりとか」「むしろほっとくとオートクリーンされちゃうファイルを追いかけるときとか😆」「たしかにたしかに」

ビューでbyebugを使うコツ(Hacklinesより)


つっつきボイス:「めっちゃ短い記事です」「<%# a = 1; byebug; b = 2; %>とか書くとbyebugがビューで動くと」「こういう適当な代入文ではさまないとbyebugがするっと通り抜けちゃうらしいです😆」「そうなんだ!」

「ちょうど今日のWebチームミーティングでRailsのビュー周りの発表がありましたけど、ビューだとプリコンパイルしてメソッド化されてからみたいなことをやってたりするから、そういうのもbyebugに影響したりするのかな?☺️」「そんな予感はしますね🤔」「自分はビューだとbyebugよりも適当にraiseして調べますけどっ😆」「私はprintデバッグしかしてないけどっ😆」

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

Railsでunscopedしない方法

unscopedはしない方がいいっすね〜: マジ事故るから🤣」「そういえば前に翻訳した記事でもそんな主張してました😆」「自分がデータベースVIEW↓をおすすめする理由がまさにそれ😋: データベースVIEWならunscopedしてもデフォルトスコープが外れても大丈夫ですし👍」

RDBMSのVIEWを使ってRailsのデータアクセスをいい感じにする【銀座Rails#10】

Rackミドルウェアで使われているデザインパターンを探る(Ruby Weeklyより)


同記事より


つっつきボイス:「Rackミドルウェアのデザインパターンか」「この辺のcallメソッド↓とかはよく言及されますね」「callメソッドさえ実装すればRackアプリになれるとか☺️」

# 同記事より
class MyApp
  def call(request)
     [ 200, {"Content-type" => "text/plain"}, ["hello world"]]
  end
end
app = MyApp.new

参考: Rails と Rack - Rails ガイド
参考: 10分でわかる Rack ミドルウェアの作り方 | MMMブログ

「なんだかLISPっぽいコードが、と思ったらClojureだった😆」

(defn wrap-content-type [handler content-type]
  (fn [request]
    ;; call the next handler, then add a header to the response
    ;; before returning it to our caller
    (let [response (handler request)]
      (assoc-in response [:headers "Content-Type"] content-type))))

参考: Concepts · ring-clojure/ring Wiki
参考: Clojure - Wikipedia


clojure.orgより

「たしかにRackはChain of Responsibilityパターンだなぁ」「Chain of ResponsibilityパターンってGoFでしたっけ?」「マルチスレッド編にあったかな?」「(内職中顔を上げて)GoFにあるっ、たしか基本の23パターンにあったと思うし」

参考: Chain of Responsibility パターン - Wikipedia

「Chain of Responsibilityパターンということは、ミドルウェア間のinとoutで示し合わせてチェインできるようにするとそういうヤツでしたっけ」「そうそう、自分が処理できるなら自分のcallメソッドを使うけど、自分で処理できないならそのまんま次のチェインにリクエストごと渡すと☺️」「後は任せたっと😆」「もうぽいっと投げる🏈」「で、チェインの最後に何でも食べるヤツを番兵的に置いて引き取ってもらうと🎖」

参考: 番兵 - Wikipedia

「記事にはDecoratorパターンもあるし」「RackはPipelineパターンも使ってるとあるけど聞いたことない…」「それ知らね〜😆」「少なくともGoFではない😆」

参考: Decorator パターン - Wikipedia
参考: The Pipeline Pattern — for fun and profit - Aaron Weatherall - Medium

「この辺↓とかたしかにパイプラインっぽいし👀: 戻ってこないけど😆」「パイプラインなら戻らなくてもよさそうだけど😆」「またしかに😆」

Tools::Pipeline.new .add_step(Tools::GetPolicyHistory)
.add_step(Tools::WithLoggedOutcome, log) 
.add_step(Tools::RejectIfUnsupportedCheckPolicyHistory) 
.add_step(Tools::FlagWithinRetentionIfEL) 
.add_step(Tools::RejectIfNotFlaggedWithinRetention)
.add_step(Tools::RejectIfActiveChainNotYetMigrated)
.add_step(Tools::GetCustomerFromRails, rolodex_api)
.add_step(Tools::MakeChain, pimms_api)
.add_step(Tools::AssociateCustomerWithChain, rolodex_api)
.add_step(Tools::MarkCompletedAfterwards, log, pimms_api)
.add_step(Tools::MigratePoliciesAndNotes, log, pimms_api, notes_api)
.call(old_policy_number: old_policy_number)

VCR gemの使いこなし10(Ruby Weeklyより)

# 同記事より
VCR.configure do |c|
  vcr_mode = ENV['VCR_MODE'] =~ /rec/i ? :all : :once

  c.default_cassette_options = {
    record: vcr_mode,
    match_requests_on: %i[method uri body]
  }
end

つっつきボイス:「先週ちらっと触れたVCR gem(ウォッチ20190708)の使いこなし記事です」「use_cassetteとかありますね☺️」「やっぱりVCRだけにカセットなんですね😆」

「VCRはハードウェアのテストとかやるのに向いてる感じですね😋」「あんまりスタブ化されてないサービスなんかをカセットにするといいかも」「そういえばFaradayしか使えないこととかあった😆」「Faradayはちょい重いんだよな〜😅」

参考: lostisland/faraday: Simple, but flexible HTTP client library, with support for multiple backends.

「そのときはVCRでカセットに記録してそれでテストしましたし」「そっちの方が結局テスト書きやすいですし😋」「ぼくは雑だからスタブ使っちゃいますが😆」「スタブって個人的に何となく信用しきれないところがあって、生リクエスト作りたくなっちゃうな〜」「自分もそうしますけど😆」「スタブは軽いしテスト範囲を限定できるから、いいといえばいいんですが」

ソフトウェアテストでstubを使うコストを考える(翻訳)

RailsアプリのDockerコンテナをビルドしてみた(Hacklinesより)

つっつきボイス:「Dockerfileとdocker-composeでRailsというみんなやってそうな記事ですが、どうやってるのかなと思って😆」「RailsでDockerを使ってる人って、どの人も微妙〜に使い方違うんですよね🤣」「nodejsをapt-getでインストールしてるあたり↓とかも人によって違うし😆」「何が入ってくるのかわかりにくいヤツ😆」

FROM ruby:2.6.2
RUN apt-get update -qq && apt-get install -y nodejs postgresql-client

「本当に小さいコンテナを作りたければ本チャンのWebサーバーにはnodeはいらないはずだし: Webpackerをコンパイルするときだけあればいいんじゃね?って😆」

「おや、この記事のdocker-composeはvolumeの下にcachedがついてないじゃないですか🤣」「そうそう、DockerfileもRUN apt-get updateした後にaptのキャッシュを消してないし🤣」「マサカリ飛んできたゾ🤣」

「ちょっと前にdocker-composeのvolumeにcachedとつけると速くなることを学びましたよ〜😂」「最近入ってきた機能ですよね☺️」「たしかこれやらないと、特にDocker for Macで遅くなる」「付ける分にはどのDockerでもいいんでしょうか?」「今はどれでも使えると思います☺️」

参考: Docker for Mac のボリュームの遅さを cached オプションで解消 - Qiita

「この記事の人ももしかするとLinux環境なのかもしれないし😆」「そこは情状酌量で😆」


後でDocker公式ドキュメントのベストプラクティスをmorimorihogeさんが見つけてくれました。

参考: Dockerfile のベストプラクティス — Docker-docs-ja 1.9.0b ドキュメント

以下は よく練られた RUN 命令であり、推奨する apt-get の使い方の全てを示すものです。

# docs.docker.jpより
RUN apt-get update && apt-get install -y \
    aufs-tools \
    automake \
    build-essential \
    curl \
    dpkg-sig \
    libcap-dev \
    libsqlite3-dev \
    lxc=1.0* \
    mercurial \
    reprepro \
    ruby1.9.1 \
    ruby1.9.1-dev \
    s3cmd=1.1.* \
 && apt-get clean \
 && rm -rf /var/lib/apt/lists/*

付け加えると、apt キャッシュをクリーンにし、 /var/lib/apt/lits を削除することで、イメージのサイズを減らします。 RUN 命令は apt-get update から開始されるので、 apt-get install でインストールされるパッケージは、常に新鮮なものです。
docs.docker.jpより

その他Rails


つっつきボイス:「おっ、MySQLの内部表現か😋」

「これ↓は…なんだbeforeか😆」

YEAR
1バイトinteger
DATE
3バイトintegerをYYYY×16×32 + MM×32 + DDをパック
TIME
3バイトintegerをDD×24×3600 + HH×3600 + MM×60 + SSをパック
TIMESTAMP
4バイトinteger、epoch (‘1970-01-01 00:00:00’ UTC)以降のUTC秒を表す
DATETIME
8バイト、うち4バイトintegerはYYYY×10000 + MM×100 + DDをパックしたdate、 4バイトはHH×10000 + MM×100 + SSをパックしたtime

「そしてafterはこれ↓」「hourが10ビット」「minuteとsecondはそれぞれ6ビットに😳」「DATETIMEはyear*13+month、あ、month表現でyearを持つってことか」「へぇ〜😳」

# TIME
 1 bit sign    (1= non-negative, 0= negative)
 1 bit unused  (reserved for future extensions)
10 bits hour   (0-838)
 6 bits minute (0-59) 
 6 bits second (0-59) 
---------------------
24 bits = 3 bytes

# TIMESTAMP: エンディアンがlittleからbigになった他は同じ

# DATETIME
 1 bit  sign           (1= non-negative, 0= negative)
17 bits year*13+month  (year 0-9999, month 0-12)
 5 bits day            (0-31)
 5 bits hour           (0-23)
 6 bits minute         (0-59)
 6 bits second         (0-59)
---------------------------
40 bits = 5 bytes

「これはきれいに寄せたな〜😋」「きつきつのパンツみたくなりましたね😆」

Ruby

time_calc: 新しい日時算出ライブラリ(Ruby Weeklyより)

# 同リポジトリより
require 'time_calc'

TC = TimeCalc

t = Time.parse('2019-03-14 08:06:15')

TC.(t).+(3, :hours)
# => 2019-03-14 11:06:15 +0200
TC.(t).round(:week)
# => 2019-03-11 00:00:00 +0200

# TimeCalc.call(Time.now) shortcut:
TC.now.floor(:day)
# => beginning of the today

「モンキーパッチなし」「マジックなし」「チェイン可能」だそうです。


つっつきボイス:「zverokさんが新たに日時算出ライブラリを作ったそうなんですが、どの辺がうれしいかなと思って」「ああ、日時のround(四捨五入)とかfloor(切り下げ)とかceil(切り上げ)ってたしかに欲しいときある!」「おお!」「floorceilまであるって😆」

「こういうのやりたくなったりしません?週単位の切り上げとか、月単位の切り捨てとか😆」「Dateでroundの場合って、どう丸めるんだろう?🤔」「水曜日のある時点を越えたときだったりして😆」「その辺をカスタマイズできたりすると業務的にかなりうれしいかも😋」「こういうの割と自力で書いたりしてますもんね😆」「stepしたりチェインしたりできるし😳」

# 同リポジトリより
TC.(t).step(2, :weeks)
# => #<TimeCalc::Sequence (2019-03-14 08:06:15 +0200 - ...):step(2 weeks)>
TC.(t).step(2, :weeks).first(3)
# => [2019-03-14 08:06:15 +0200, 2019-03-28 08:06:15 +0200, 2019-04-11 09:06:15 +0300]
TC.(t).to(Time.parse('2019-04-30 16:30')).step(3, :weeks).to_a
# => [2019-03-14 08:06:15 +0200, 2019-04-04 09:06:15 +0300, 2019-04-25 09:06:15 +0300]
TC.(t).for(3, :months).step(4, :weeks).to_a
# => [2019-03-14 08:06:15 +0200, 2019-04-11 09:06:15 +0300, 2019-05-09 09:06:15 +0300, 2019-06-06 09:06:15 +0300]

「Ruby本家とかActive Supportに影響を与えそうな気がちょっとしてきました」「Active Supportに入っててもおかしくない機能かも😍」「この実装をそのまま使うかどうかはわからないけど😆」

その他Ruby

つっつきボイス:「bundle execを入力したくなくてやってみた記事だそうです」「binスタブ使うとかじゃなくて?」「記事ではzshでやってますね」「Oh My Zsh↓を入れるといい感じにできるぞ!みたいな😆」「そんなのあったとは😆」


ohmyz.shより

「いっとき、Railsコマンドを実行するのにbundle execをいちいち入力したくない人たちがいろいろやって不具合出したりしてましたね」「beあたりをbashのエイリアスにすればいいんじゃね?☺️」「私もそれで十分だと思います😆」


Quoraより


つっつきボイス:「『お金があればやれる』という結論が沁みました😂」「インタプリタだしな〜しょうがないかな〜」



つっつきボイス:「Matzの回答でとても勇気が湧きました😂」「集中力15分!😆」「よくて30分」「普通8時間も集中力とか続かないってホント😆」

「8時間も集中を持続するなんて、せいぜい数年に一度、それも爆発炎上🔥したときぐらいだし😆」「それも明け方とかに😆」


前編は以上です。

バックナンバー(2019年度第3四半期)

週刊Railsウォッチ(20190604-2/2後編)Cloudflare Workers KVの可能性、PostgreSQL 12 Beta 1、Bootstrap 5でjQuery廃止ほか

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

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

Rails公式ニュース

Ruby Weekly

Hacklines

Hacklines

デザインも頼めるシステム開発会社をお探しならBPS株式会社までどうぞ 開発エンジニア積極採用中です! Ruby on Rails の開発なら実績豊富なBPS

この記事の著者

hachi8833

Twitter: @hachi8833、GitHub: @hachi8833 コボラー、ITコンサル、ローカライズ業界、Rails開発を経てTechRachoの編集・記事作成を担当。 これまでにRuby on Rails チュートリアル第2版の監修および半分程度を翻訳、Railsガイドの初期翻訳ではほぼすべてを翻訳。その後も折に触れて更新翻訳中。 かと思うと、正規表現の粋を尽くした日本語エラーチェックサービス enno.jpを運営。 実は最近Go言語が好きで、Goで書かれたRubyライクなGoby言語のメンテナーでもある。 仕事に関係ないすっとこブログ「あけてくれ」は2000年頃から多少の中断をはさんで継続、現在はnote.muに移転。

hachi8833の書いた記事

BPSアドベントカレンダー

週刊Railsウォッチ