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

週刊Railsウォッチ(20170728)bootsnapがRailsで正式採用、Ruby Prizeの推薦開始、PostgreSQL配列の重複を除去ほか

$
0
0

こんにちは、hachi8833です。南極の氷が増えているのか減っているのか、だんだんよくわからなくなってきました。

7月最後のRailsウォッチ、いってみましょう。

Rails 5.1.3rc2と5.0.5rc2がリリース(Rails公式ニュースより)


rubyonrails.orgより


つっつきボイス: 「バグフィックスが中心だろうし、正式版来たらアップグレードするかな」「先週rc1リリースから早くもrc2です」「そういえばRubyコミュニティには、rcいくつまでをどういうタイミングで出すみたいな目安ってあるんだろうか」

Ruby Prize 2017の推薦開始


www.ruby.or.jpより


つっつきボイス: 「Ruby Prize 2017は毎年Ruby World Conferenceの場で発表されていますね」「Ruby Prizeはソフトウェアや企業ではなく、個人を対象にした賞であるところが特徴」「(今さら要項を見て)ほんとだ」


「社内では毎年話してる気がするんだけど、こういう個人向けの賞が長年運用されているところがRuby界隈のコミュニティのいいところだなーと思ってます」「確かに他の言語ではこういうのあまり見ないかも」「contributeする個人に陽のあたる場所を提供する場というのは本当に大事」「何も見ずに言うけど、昨年の受賞者にたしかOpenSSL bindingをひたすらアップデートした学生さんがいたんじゃなかったかな: こういう地味な作業って普通はあまり表に出てこない」

「OpenSSLのアップデートってバグか何か?」「いやいや、元のAPIがガラッと変わってbindingがほぼ書き直しを迫られたということ」


賞が欲しくてcontributeする人はあまりいないと思いますが、そういう人たちに報いる場があることで張り合いが出るのはいいですね。

TechRachoを1年近くやってて痛感していますが、ポジティブフィードバックってめったに当事者には伝わりません。いいと思ってくれる人は何も発しないことがほとんどなので。

というわけで、皆さまもがんがんRuby Prizeに推薦を送ってあげてください

↑もうちょっと小さくしたいんだけどなこれ。

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

新機能: bootsnap gemがRails標準に

以前のRailsウォッチでご紹介したy-yagiさんの予想どおりになりました。

# Gemfile
+# for railties app_generator_test
+gem "bootsnap", ">= 1.1.0", require: false

つっつきボイス: 「おー、Railsに組み込むのかと思ったらgemのまま取り込まれたのか」「bootsnapってどうしてもbootstrapに見えてしまってw」

改修: rails dbconsoleコマンドでの環境指定で-eが必須になった

+    Previously:
+        $ bin/rails dbconsole production
+    Now:
+        $ bin/rails dbconsole -e production

つっつきボイス: 「たしかにこういうのはオプション付きで指定するほうがいいかも」

修正: カスタムのテーブルエイリアス名でのwhereの挙動

kamipoさんの修正です。SELECT句でカスタムのテーブルエイリアス名を使えるように修正されました。

# activerecord/lib/active_record/relation/query_methods.rb
        if select_values.any?
           arel.project(*arel_columns(select_values.uniq))
         else
-          arel.project(@klass.arel_table[Arel.star])
+          arel.project(table[Arel.star])
         end
       end

つっつきボイス: 「(ドラフトを眺めて)custom table alias nameって、『カスタムテーブルのエイリアス名』じゃなくて『カスタムのテーブルエイリアス名』じゃないかな?」「おっと失礼しました: 英語って助詞がないからこういう区切りってわかってる人じゃないとパースしづらいですね」「まあそれは日本語もそうですけどね」


「情報処理安全確保支援士合格者御一行様歓迎列車到着遅延見込」や「東京大学理学部地質学鉱物学教室施設定例保守点検実施中止経過報告」みたいに漢字だらけだと、外国人でなくてもつらそうです。メソッドチェーンでこんなのやったらMR通らなさそう。

参考: Wikipedia-ja 般若心経

修正: エンコードエラーのメッセージに表示される無効なUTF-8文字をscrubするようにした

無効な文字のせいでCIがコケることがあったそうです。

# actionpack/lib/action_dispatch/request/utils.rb
          unless params.valid_encoding?
             # Raise Rack::Utils::InvalidParameterError for consistency with Rack.
             # ActionDispatch::Request#GET will re-raise as a BadRequest error.
-            raise Rack::Utils::InvalidParameterError, "Non UTF-8 value: #{params}"
+            raise Rack::Utils::InvalidParameterError, "Invalid encoding for parameter: #{params.scrub}"
           end
         end
       end

つっつきボイス: 「こういう修正は地味にありがたいかも」

self が不正なバイト列を含む場合に別の文字列に置き換えた新しい文字列を返します。
不正なバイト列を置き換える文字列を指定します。省略した場合は self の文字エンコーディングが Encoding::UTF_16BE, Encoding::UTF_16LE, Encoding::UTF_32BE, Encoding::UTF_32LE, Encoding::UTF_8 のいずれか の場合は “\uFFFD” を表す文字で、それ以外の場合は “?” で置き換えられます。ブロックが指定された場合は不正なバイト列はブロックの戻り値で置き換えられます。
Ruby リファレンスマニュアル: instance method String#scrubより

つっつきボイス: 「ところで、invalidは「不正な」と訳されることが多いんですが↑、私はvalid/invalidは『有効な』『無効な』って訳すようにしてます」「言われてみれば、技術用語のinvalidって『不正送金』みたいな違法なニュアンスは全然ないはずだから、『有効/無効』の方が的確だなー」「実は某社の翻訳スタイルガイドにそういう指示があったのでしたw」

改修: rails yarn:installによるリビルドをproductionだけに限定

開発中にyarn installするたびにネイティブパッケージがリビルドされて時間がかかっていたのが修正されました。

# railties/lib/rails/tasks/yarn.rake
namespace :yarn do
   desc "Install all JavaScript dependencies as specified via Yarn"
   task :install do
-    system("./bin/yarn install --no-progress")
+    system("./bin/yarn install --no-progress --production")
   end
 end

yarn install呼び出しのたびにネイティブパッケージがリビルドされます(yarnpkg/yarn#932)。私たちのところでカスタムフォントをビルドするためにdevDependenciesでnodeのネイティブパッケージを使ったところ、それまで2秒で済んだデプロイがリビルドのせいで30秒以上かかるようになってしまいました。Railsではassets:precompileの前にyarn:installが使われますが、このパッチをあてるとdev dependencyが無視されるようになります。bin/yarnを実行すれば従来どおりすべてがインストールされます。
とりあえず問題を回避したい場合: bin/yarnを以下のように更新してください。

# ...
  begin
    no_dev = %w[production staging].include?(ENV['RAILS_ENV'])
    exec "yarnpkg #{ARGV.join(' ')} #{'--prod' if no_dev}"

# 29851より

RailsアプリにSalesforce APIを統合する(RubyFlowより)


www.railscarma.comより

restforceというgemを使ってSalesforceのAPIを使えるようにする記事です。


つっつきボイス: 「ぱっとタイトルだけ見たとき、Salesforceが自社APIをRailsアプリ化したのかと思っちゃいました」「んなこたーないw」「まあ、よくある記事ではある」

ClojureのinterposeをRubyで実装したった(RubyFlowより)


bcobb.netより

ClojureのinterposeをRubyでも使いたくて実装したのだそうです。

# bcobb.netより

{one: 1, two: 2, three: 3}.interpose(:sep).to_a
# [[:one, 1], :sep, [:two, 2], :sep, [:three, 3]]

StringIO.new("line one\nline two\nline 3\n").interpose("|").to_a
# ["line one\n", "|", "line two\n", "|", "line 3\n"]

つっつきボイス: 「Clojureのこのinterposeって、配列要素の間に挿入するってことか」「Rubyならもっとスマートに書く方法がありそうな気がして仕方がない」「これが便利なものならActiveSupportあたりにありそうなものだけど、見当たらないということは…w」

interpose:
{他動-1} : 〜を間に入れる[置く]
{他動-2} : 〔他の人が話をしているときに言葉・質問・意義などを〕差し挟む


Clojureって、ついClosureと打ち間違えてしまいます(打ち間違えました)。

HerokuでDBサーバーの負荷を80%軽減した


schneems.comより

$ heroku pg:outliers
total_exec_time  | prop_exec_time |   ncalls    |   sync_io_time   |                                                                                       query
------------------+----------------+-------------+------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
3790:50:52.62102 | 80.2%          | 100,727,265 | 727:08:40.969477 | SELECT  ? AS one FROM "repos" WHERE LOWER("repos"."name") = LOWER($1) AND ("repos"."id" != $2) AND "repos"."user_name" = $3 LIMIT $4
493:04:18.903353 | 10.4%          | 101,625,003 | 52:09:48.599802  | SELECT COUNT(*) FROM "issues" WHERE "issues"."repo_id" = $1 AND "issues"."state" = $2

つっつきボイス: 「うん、新しい話は何もないw」「そっかなーと思ったらやっぱりそうでしたか」「記事の問題じゃなくて、そもそもパフォーマンスチューニングってそういうものです: 地道な作業の積み重ねなので」

Autoprefixer CSS online: Autoprefixerの更新がつらい人向けのサービス

社内Slackからです。


autoprefixer.github.ioより


つっつきボイス: 「vendor prefixとかイヤやーw: Railsならcompass使えばいいんではないかと」「vendor prefixってホントその場しのぎの技術ですねw」


compass-style.orgより

Screencast: Codemy.netの「Rails APIシリーズ」(RubyFlowより)

Railscastsが開拓して以来(英語圏では)過熱が続くscreencast界にまた名乗りを上げてきたサイトです。思ったより動画がたくさんあります。APIサーバーに絞っているようですが、ひととおりカバーしている様子。
最初の動画が半年前の公開なのでかなり新しい方ではあります。


つっつきボイス: 「サムネイル画像が同じすぎてわかりにくいのー」「(ひとつ開いてみて)あー、transcript(文字起こし)が全然ないwこれはアカンなー: 動画の垂れ流しサイト観るのはよっぽど暇なときぐらいしか考えられないし」「5分の動画でも5分かけて見る気にはなれないですねー」「フィードバックしときます」

そういえば日本ではscreencastってさっぱり見かけませんね。別にいいんですが。

そこからCode Schoolのよさについての話題になりました。


つっつきボイス: 「Code Schoolの動画部分って見ます?」「見ないww: Code Schoolは動画より本編のできがずっといいので」「あのクォリティで本編作るのはそうそうできることじゃない、いやほんとに」


www.codeschool.comより

追記

たった今(日本時間で14時)Codemy.netに登録して「文字起こし、ほすいー」とチャットで伝えたところ、こんな時間なのに即レスがありました。

フィードバックありがとう! 実は今のサイトで文字起こしを置けないとかいろいろ制約があって困っていたので、サイト引越しの準備しているところ

というわけで今後に期待したいと思います。クレーム口調でない、改善につながるフィードバックを送ると、どこでも本当に喜んでもらえますね。

ベンチマーク結果をRubyでかっこよくグラフ化


codeship.comより

gruff gemを使っています。


codeship.comより


つっつきボイス: 「PythonにはCIのベンチマーク結果を継続的に掲載している公式のspeed.python.orgがあるんですが、Rubyにもそういう継続的なベンチサイトってありましたっけ?」「どうだったかなー、MRIやJRubyやmrubyといった実装ごとにベンチを比較するサイトならあったような気がするんだけど」

ざっとぐぐった限りではそれらしいものは見当たらず、そこからgraphという言葉についての話題になりました。


つっつきボイス: 「この記事でgraphという言葉を使っているのがすごく珍しいと思う」「あ、確かに: 技術英語サイトでは普通こういうのってchartって書きますよね」「graphっていうとグラフ理論の方を指す感じ」「そうそう、有向グラフとか」「もしかするとアメリカ英語とイギリス英語の違い?」

と思ってその場で各種辞書を繰ってみたのですが、意外にも辞書・シソーラスともに米英の違いがなく、「graphの類義語がchart」とのことでした。

つっつきボイス: 「chartとgraphってどう使い分けるんだろうw 謎」「謎」


日本語で言うとさしずめ「図」と「画像」ぐらいの違いなのかなと、何となく想像してしまいました。

Officetxt gem: 英語圏のフリーライター向けコマンドツール集(RubyFlowより)


github.com/officetxtより

これ自体は★は少ないですが、ツールの中でWordをMarkdownに変換するword-to-markdownというgem(★800個近く)が気になりました。日本語とおるでしょうか。

LibreOfficeが必要だそうです。

gem install word-to-markdown
file = WordToMarkdown.new("/path/to/document.docx")
=> <WordToMarkdown path="/path/to/document.docx">

file.to_s
=> "# Test\n\n This is a test"

file.document.tree
=> <Nokogiri Document>

つっつきボイス: 「単独のテキストツールじゃなくていろんなツール(gem)を一発でインストールするのか」「docxはxmlだからできそうダナ」


www.libreoffice.orgより

Deviseでユーザーを無効にする(RubyFlowより)


blog.kodius.ioより

短い記事です。

# https://blog.kodius.io/2017/07/26/how-to-deactivate-user-rails-with-devise/ より
class AddDeactivatedToUsers < ActiveRecord::Migration
  def change
    add_column :users, :deactivated, :bool
  end
end

# user.rb
def destroy
  update_attributes(deactivated: true) unless deactivated
end

def active_for_authentication?
  super && !deactivated
end

つっつきボイス:lockableでやればよさそうだけど、どうしてもカスタマイズしないといけない事情があったのかな」「こうやってフラグ立てるのってあんまりよくなさそうですね」「必ずしもそうとは限らない: 認証システムをカスタマイズ一切なしで導入できる状況って現実にはほとんどないんですよ」「うーむ」

参考: Devise Wikiシリーズ総もくじ: アカウント削除後のUserデータを保存する(論理削除)

補足

Stack Overflowには「Deviseのactive_for_authenticationでできた」「認証(authentication)ではなく承認(authorization)でやるべき」などの情報もありました。

プレゼン「TC39、ECMAScript、JavaScriptの未来」

中国の深セン経済特区で今年6月に開催されたTFC: Tencent Frontend Conferenceのプレゼンです。


tfc.alloyteam.comより

frontendが中国語で前端って書かれてるところにほだされて、つい拾ってしまいました。ECMAScriptsの最新プロポーザルが多数紹介されています。

# tfc.alloyteam.comより
[1, 2].indexOf(2) !== -1 // true
[1, 2].indexOf(3) !== -1 // false
[1, 2].includes(2) // true
[1, 2].includes(3) // false

つっつきボイス: 「TC39って何だろ」「(ググって)あー、ECMAの分科会か」「マジェスティックトゥエルブみたいw」

dnsimple.com: ドメイン管理アプリ


dnsimple.comより

require "dnsimple"
client = Dnsimple::Client.new(access_token: "abc123")

# Define an account id
account_id = 1010

# List your domains
puts client.domains.list_domains(account_id).data

# Create a domain
response = client.domains.create_domain(account_id, name: "example.com")

# Get a domain
response = client.domains.domain(account_id, "example.com")
puts response.data

同社のブログによるとHanamiで構築されているそうです。


つっつきボイス: 「こういうサービスってインフラエンジニア的にはどうでしょう?」「いろいろ機能はあるみたいだけど、AWSのRoute53使えばええやんw」

PostgreSQL

PostgreSQLのペネトレーションテストガイド

# medium.com/@cryptocracker99 より
postgres=# CREATE TABLE pentestlab (t TEXT);
postgres=# INSERT INTO pentestlab(t) VALUES('nc -lvvp 2346 -e /bin/bash');
postgres=# SELECT * FROM pentestlab;
postgres=# COPY pentestlab(t) TO '/tmp/pentestlab';


medium.com/@cryptocracker99より


つっつきボイス: 「ペネトレーションテストっていわゆる侵入テストですよね」「侵入テスト以外に、どこまでやったらサーバーが死ぬかを試すという意味のペネトレーションテストもあります」「これはどっちかな: nc打ってるし、まさに侵入かけようとしてる」

PostgreSQLの配列から重複を取り除く

-- https://medium.com/the-falconry より
SELECT
  song_id,
  UNNEST(STRING_TO_ARRAY(STRING_AGG(v.genre_list, ', '), ', ')) AS genre
FROM versions v
GROUP BY song_id
  1. 分野ごとに集約
  2. UNNESTで配列のネストを解除(↑上のコード)
  3. DISTINCTが使えるようになる
  4. 仕上げ

つっつきボイス: 「Rubyなら#flattenして#uniqするところをPostgreSQLでやってるのかー」

PostgreSQLのインデックス肥大化を探る

短い記事ですがよさげです。

# pgeoghegan.blogspot.jpより
 level | l_item | blkno | btpo_flags | type | live_items | dead_items | avg_item_size | page_size | free_size | distinct_real_item_keys | highkey | distinct_block_pointers
-------+--------+-------+------------+------+------------+------------+---------------+-----------+-----------+-------------------------+---------+-------------------------
     2 |      1 |   290 |          2 | r    |         10 |          0 |            15 |      8192 |      7956 |                      10 |         |                      10
     1 |      1 |     3 |          0 | i    |        285 |          0 |            15 |      8192 |      2456 |                     284 |  103945 |                     284
     1 |      2 |   289 |          0 | i    |        285 |          0 |            15 |      8192 |      2456 |                     284 |  207889 |                     284
     1 |      3 |   575 |          0 | i    |        285 |          0 |            15 |      8192 |      2456 |                     284 |  311833 |                     284
     1 |      4 |   860 |          0 | i    |        285 |          0 |            15 |      8192 |      2456 |                     284 |  415777 |                     284
     1 |      5 |  1145 |          0 | i    |        285 |          0 |            15 |      8192 |      2456 |                     284 |  519721 |                     284
     1 |      6 |  1430 |          0 | i    |        285 |          0 |            15 |      8192 |      2456 |                     284 |  623665 |                     284
     1 |      7 |  1715 |          0 | i    |        285 |          0 |            15 |      8192 |      2456 |                     284 |  727609 |                     284
     1 |      8 |  2000 |          0 | i    |        285 |          0 |            15 |      8192 |      2456 |                     284 |  831553 |                     284
     1 |      9 |  2285 |          0 | i    |        285 |          0 |            15 |      8192 |      2456 |                     284 |  935497 |                     284
     1 |     10 |  2570 |          0 | i    |        177 |          0 |            15 |      8192 |      4616 |                     177 |         |                     177
     0 |      1 |     1 |          1 | l    |        367 |          0 |            16 |      8192 |       808 |                     366 |     367 |                       6
     0 |      2 |     2 |          1 | l    |        367 |          0 |            16 |      8192 |       808 |                     366 |     733 |                       6
     0 |      3 |     4 |          1 | l    |        367 |          0 |            16 |      8192 |       808 |                     366 |    1099 |                       6
...
     0 |   2730 |  2741 |          1 | l    |        367 |          0 |            16 |      8192 |       808 |                     366 |  999181 |                       6
     0 |   2731 |  2742 |          1 | l    |        367 |          0 |            16 |      8192 |       808 |                     366 |  999547 |                       6
     0 |   2732 |  2743 |          1 | l    |        367 |          0 |            16 |      8192 |       808 |                     366 |  999913 |                       6
     0 |   2733 |  2744 |          1 | l    |         88 |          0 |            16 |      8192 |      6388 |                      88 |         |                       2
(2744 rows)

つっつきボイス: 「MySQLでは辛みも含めていろいろ経験積めたんで、そろそろPostgreSQLのインデックス周りの深いところもやってみるか」

CSS記事

margin corruption問題の対処方法


bitsofco.deより

良記事です。クイズも付いています。

「margin corruption」は日本語に定訳がない感じで、「marginが親要素に効いてしまう」「画像の下に謎の余白ができる」など完全にバラついているようです。

参考

CSS Variablesはこう扱え

madebymike.com.auより

CSS Variablesはカスタムプロパティとも呼ばれる、CSSのかなり新しい仕様です。

/* madebymike.com.au より*/

/* これは変数宣言 */
.thing {
  --my-var: red;
}
/* これはプロパティ宣言 */
.thing {
  background: var(--my-var);
}

つっつきボイス: 「(CanIUseを開きながら)IE対応なし、と↓」

http://caniuse.com/css-variables/embedより

オピニオン: Rustは「自動運転のC++」だ


www.rust-lang.orgより

短いのですぐ読み終わると思います。

実地のRustコードが見たかったので、この間のRailsウォッチで取り上げたdanielpclark/faster_pathから引用しようかなと思っていたところ「んー、このコードだとあんまりRustっぽくないっすねー」というツッコミがBPSアプリチームの方から聞こえてきたので、おすすめいただいたRustコードに差し替えました。

# https://github.com/BurntSushi/ripgrep/blob/master/src/worker.rs より
...
impl Worker {
    /// Execute the worker with the given printer and work item.
    ///
    /// A work item can either be stdin or a file path.
    pub fn run<W: WriteColor>(
        &mut self,
        printer: &mut Printer<W>,
        work: Work,
    ) -> u64 {
        let result = match work {
            Work::Stdin => {
                let stdin = io::stdin();
                let stdin = stdin.lock();
                self.search(printer, Path::new("<stdin>"), stdin)
            }
            Work::DirEntry(dent) => {
                let mut path = dent.path();
                let file = match File::open(path) {
                    Ok(file) => file,
                    Err(err) => {
                        if !self.opts.no_messages {
                            eprintln!("{}: {}", path.display(), err);
                        }
                        return 0;
                    }
                };
                if let Some(p) = strip_prefix("./", path) {
                    path = p;
                }
                if self.opts.mmap {
                    self.search_mmap(printer, path, &file)
                } else {
                    self.search(printer, path, file)
                }
            }
        };
        match result {
            Ok(count) => {
                count
            }
            Err(err) => {
                if !self.opts.no_messages {
                    eprintln!("{}", err);
                }
                0
            }
        }
    }
...

つっつきボイス: 「BurntSushiってなんちゅう名前w」「黒焦げw」「C++の有力な代替候補が不在の時期が長かったけど、Rustの隆盛で勢いづいてきたかな」


自動運転機能がなかった時代は人間が運転する以外の選択肢はありえませんでしたが、自動運転が普及して二世代も過ぎれば「えー!昔は人間が自動車を運転してたの!?それって危なくね?」となること請け合いですね。

参考: Wikipedia-ja: お猿の電車

書籍: XUnit Test Patterns


xunitpatterns.comより

morimorihogeさんが以下の図を見つけてくれたときのサイトを辿って見つけました。「XUnit Test Patterns」の第二版で使われる予定の図だったようですが、第二版はまだ刊行されていないようです。


xunitpatterns.comより

Go言語ではパッケージレベルの変数や関数内部での初期化を使わないこと

短いですが良記事です。関数に内部状態を埋め込むべきでない、と言われて反省しました。

// http://peter.bourgon.org/blog/2017/06/09/theory-of-modern-go.htmlより
func NewObject(n int) (*Object, error) {
    row := dbconn.QueryRow("SELECT ... FROM ... WHERE ...")
    var id string
    if err := row.Scan(&id); err != nil {
        logger.Log("during row scan: %v", err)
        id = "default"
    }
    resource, err := pool.Request(n)
    if err != nil {
        return nil, err
    }
    return &Object{
        id:  id,
        res: resource,
    }, nil
}

つっつきボイス: 「文法がっちり固まってるはずのGo言語にもmodernとかあるのかww」「本家のコードでは、関数が返すエラーをokっていう変数で受ける慣習があるんですが、あれだけはイヤですw」

ITエンジニアが健康を保つための5つのコツ: Rubyroid Labsの場合



blog.rubyroidlabs.comより

Rubyroid Labsの方から直々にお知らせいただいた記事です。

  • パソコンのモニタを見すぎない工夫をする
  • フィジカルトレーニングを続ける
  • タバコを控える
  • ストレスをためない工夫をする
  • 部屋にホコリをためない

つっつきボイス: 「面白すww」「病気の名前とか筋肉の部位みたいな名前の英語って実はほとんどわからなくってw: 腹筋って英語で何て言うんだっけというレベル」

AI同士が攻撃と防御に分かれて対決するコンテスト: セキュリティ研究の一環


www.technologyreview.comより

つっつき後に見つけたMITテクノロジーレビューの記事です。kaggle.comによると、「対象を特定しない敵対的攻撃」「対象を特定する敵対的攻撃」「敵対的攻撃からの防御」部門で募集したAIたちが対決するコンテストを今年12月のNIPS 2017期間中に開催するそうです。

「対象を特定しない攻撃」というと軍事用語のゲリラ戦(遊撃戦)を連想してしまいました。

皆さまもどうかAIから敵認定されませんように。

ツイートより

番外

Microsoftのニューラル翻訳サイト



translator.microsoft.com/neuralより

右と左で違う訳が出てくるのですが、もしかすると一方がニューラル翻訳でもう一方が旧来の機械翻訳ではなかろうかと根拠もなく思ったりしました。それだけです。

Windowsのmspaint.exeが終了


www.bbc.comより

仮面ライダーシリーズ、次は「ビルド」


今週は以上です。

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

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

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

Ruby 公式ニュース

Rails公式ニュース

RubyFlow

160928_1638_XvIP4h


CSSでの句読点ぶら下げ: hanging-punctuationプロパティ

$
0
0

句読点のぶら下げを指定する hanging-punctuation プロパティと、超縦書ビューアでの表示例についてご紹介します。

句読点のぶら下げとは

行末に句読点が来た場合、通常の行長よりはみ出して配置する方法です。日本語の均等割り組版でよく使われます。

句読点のぶら下げ

小学校の時の作文では、この書き方をした記憶があります。

ぶら下げのメリット

もともとは、手動組版で微調整の手間を省くため、ある種の手抜きから生まれたという話もありますが、

  • 行末がより揃って見える
  • 文字間隔の調整に無理がなくなる

などのメリットもあります。紙の書籍では、それなりに多く利用されています。

hanging-punctuationプロパティ

CSS Text Module Level 3では、句読点ぶら下げを指定するための hanging-punctuation というプロパティが定義されています。

効果
none(デフォルト) ぶら下げしない
first 先頭行の開き括弧や閉じ引用符をぶら下げる(開始側にはみ出す)
last 最終行の閉じ括弧や閉じ引用符をぶら下げる
force-end 行末の句読点をぶら下げる
allow-end 他の文字間調整などで調整しきれない場合に限り、行末の句読点をぶら下げる

指定の仕方としては以下のような形になります。

p {
    text-align: justify;
    hanging-punctuation: allow-end;
}

超縦書での対応

超縦書では、2013年頃より hanging-punctuation プロパティのうち日本語組版で利用頻度の高い force-endallow-endの2つについて独自に対応しています(EPUBでは定義されていないため独自拡張扱いです)。

  • hanging-punctuation: none
hanging-punctuation: none

hanging-punctuation: none


  • hanging-punctuation: force-end
hanging-punctuation: force-end

hanging-punctuation: force-end


  • hanging-punctuation: allow-end
hanging-punctuation: allow-end

hanging-punctuation: allow-end

EPUB内のCSSにプロパティを指定することで動作します。特にベンダープレフィックス等はありません。

他の環境での対応

caniuseによると、Safariでは2016年にリリースされたバージョン10から対応しているようです。AH Formatterもかなり以前から対応していると聞いています。それ以外のブラウザでの実装は進んでいません。

なお、ブラウザCSS実装ではありませんが、Amazon Kindleも2014年頃から句読点ぶら下げが行われていたと記憶しています。

hanging-punctuationとoverflow

ぶら下げをするとブロックのサイズを超えて文字が配置されることになるため、overflowが生じます。これがInk overflowなのかScrollable overflowなのかというのは難しい問題です。

Ink overflowとは、はみ出た部分は描画されるもののサイズに反映されないもので、bow-shadowなどが該当します。対してScrollable overflowとは、サイズに反映されるもので、親の要素に収まり切らなければ overflow:auto の場合にスクロールバーが出るタイプのものです。

素直に考えれば、句読点はメインコンテンツである「文章」の一部なので、Scrollable overflowとなるように思えます(禁則ルールなどではみ出る文字はすべてScrollable overflowです)。しかし、以下のようなケースを見てみましょう。

緑色が本文を含むブロック(<p>など)の領域、グレーがぶら下げではみ出た領域です。

日本語ではいわゆる等幅フォントが広く利用されるため、句読点もグリフ分ではなく1文字分の幅を持つフォントが多いです。この場合、1文字分まるごとScrollable overflowにすると、たまたま親要素に0.5文字分のパディングがあった場合、「はみ出た句読点を含めて十分表示しきれる領域があるのに、無駄にスクロールバーが出る」という問題が発生します。このため、一度は「hanging-punctuationによるぶら下げはInk overflowとし、表示領域に適切なパディングを付与するのはページ作者の責任とする」という決定がされました。

しかし、contenteditable の場合を考えると、編集時に見えなくなってしまうのは大きな問題です。また、通常の禁則などとの相互運用を考えても、本文がInk overflowになり隠れうるのはやはり問題とされ、「Scrollable overflowだが、はみ出た部分はtrimされるべき」と言った結論に変更されたりしました。その後の議論があったのか追い切れていないのですが、実装があまり進んでいないうえ後述のとおり hanging-punctuation 自体がat-riskになったこともあり、明確になりきっていないと思います。

hanging-punctuationプロパティの今後

2017年7月現在、 hanging-punctuation プロパティはat-risk扱いとなっています。これは、CSS Text Module Level 3がドラフト段階であり、正式版までの間に削除される可能性があることを示しています。ブラウザ実装も積極的に推奨されるフェーズではないという扱いです。これには以下のような背景があります。

西洋組版での利用に関する問題提起

西洋組版における句読点ぶら下げについての問題提起が、2016年4月にサンフランシスコで開催されたF2Fで議論されました。詳しくは参考リンクとして挙げられている解説がわかりやすいですが、西洋組版では日本語よりも複雑なぶら下げルールが利用されます。例えば以下のようなものです。

  • 先頭行以外でも、開き括弧をぶら下げることがある
  • 行末でハイフンをぶら下げる際など、1文字ではなく半文字程度ぶら下げることがある
西洋組版でのぶら下げ例

西洋組版でのぶら下げ例

現行の hanging-punctuation プロパティは、元々CJKでのユースケースを元に制定された仕様なこともあり、これらの要望に対応できません。

この時点で、 hanging-punctuation プロパティへのプロパティ追加やいったん白紙に戻す案も出ましたが、すでに一部実装が存在することからLevel 4での機能追加という案が有力視されました。

より汎用的な制御への変更案

2016年9月にリスボンで開催されたTPACでのCSSWG F2Fにて、さらなる議論が実施されました。

hanging-punctuationプロパティは、1つで以下の3つを制御しているといえます。

  • どの文字をぶら下げるか
    • first では開き括弧類、 last では閉じ括弧類、 force-end, allow-end では句読点など決められています。
  • どのような条件でぶら下げるか
    • first では1行目の行頭、 last では最終行の行末、 force-end, allow-end では行末となります。
  • どれくらいぶら下げるか
    • 最大1文字固定で、半分ぶら下げるなどの制御は出来ません。

これらを1つで制御するのは筋が悪いのでは、という意見、文字種別ではなくOptical Kerningの方が適しているのではという意見などが出ました。Optical Kerningは、文字の形に合わせてカーニングを行う手法で、文字の組み合わせによってはみ出す量を変更する処理が可能になります。

Optical Kerning

これらを踏まえ、プロパティの再構築が改めて検討されましたが、既存実装もあるため現状ではドロップされず、at-risk扱いとなりました。

最終的な結論はまだ出ていませんので、他の言語や組版でのユースケース、実装面での意見などがあればまだ反映されやすいフェーズだと思います。

関連記事(CSS)

Windows: アプリ終了時テストをPowerShellスクリプトで楽に行う

$
0
0

デスクトップアプリを開発する際、アプリ起動時や終了時に低確率で発生するバグを踏むことがあります。複数のスレッドが存在し、たまたま特定のスレッドの終了処理が完了した際に発生する、などです。

具体的には、ごくまれにウィンドウの×ボタンを押してもアプリが終了しない、というバグを踏みました。10回に1回程度発生してくれれば楽なのですが、500回に1回程度発生するものがあって修正確認が面倒だったので、以下のような手抜きPowerShellスクリプトを書きました。LinuxやMacなら普通にbashスクリプトを書けば良いのですが、Windowsのスクリプトは経験がなかったので、「はじめてのPowerShellスクリプト」です。

手順

準備

事前準備として、署名のないPowerShellスクリプトを許可しておきます。管理者として起動したPowerShellで以下のコマンドを打ちます。当然セキュリティレベルを下げる操作なので理解した上でご利用ください。

set-executionpolicy unrestricted

スクリプト

不具合が出てもクラッシュやフリーズなどが後で見てわかるようなアプリであれば、あとはこんな感じのtest.ps1を作って、

$i = 0
Do {
  echo $i
  $i += 1
  Start-Process("C:/development/MyApp.exe")
  Sleep 3
  $ps = Get-Process | Where-Object {$_.Name -match "MyApp"}
  $ps.CloseMainWindow()
  Sleep 1
} While (1)

通常権限のPowerShellでtest.ps1を実行し、PCが頑張っている間に帰宅して翌日確認すればOKです。

ポイントは Stop-Process だと強制終了されてしまうので、CloseMainWindow 関数を使うことくらいです。

関連記事

Railsの`Object#try`がダメな理由と効果的な代替手段(翻訳)

$
0
0

概要

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

原文タイトルは、よくあるヨーダのセリフのもじりです。

RailsのObject#tryがダメな理由と効果的な代替手段(翻訳)

Object#tryは、Railsアプリでnil値を扱う可能性がある場合をカバーするときや、与えられたメソッドがオブジェクトで必ずしも実装されていないといった場合に柔軟なインターフェイスを提供するときに、かなりよく使われています。#tryのおかげでNoMethodErrorを回避できるのですから、これで問題はなくなったように見えます。NoMethodError例外が起きなくなったからといって、本当に問題がなくなったと言えるのでしょうか?

実際にはそうとは言えません。Object#tryにはいくつかの重大な問題がつきまといますし、たいていの場合もっとよいソリューションをかなり簡単に実装できるのです。

Object#tryのしくみ

Object#tryの基本となるアイデアはシンプルです。nilオブジェクトにメソッド呼び出しを行った場合や、そのオブジェクトにメソッドが実装されていないnilでないオブジェクトにメソッド呼び出しを行った場合に、NoMethodError例外をraiseせず、単にnilを返すというものです。

たとえば、最初のユーザーのメールアドレスを取りたいとします。ユーザーが1人もいない場合に失敗しないようにするために、次のように書くことができます。

user.first.try(:email)

このとき、さまざまな種類のオブジェクトを渡せる一般的なサービスを実装していたとしたらどうでしょう。たとえば、オブジェクトを保存した後、オブジェクトがたまたまそのための正しいメソッドを実装した場合に通知を送信するにはどうしたらよいでしょうか。Object#tryを使うと次のように書けます。

class MyService
  def call(object)
    object.save!
    object.try(:send_success_notification, "MyServiceから保存されました")
  end
end

コードを見れば、このメソッドに引数を与えられることもわかります。

途中でnilを受け取る可能性のあるステップごとに、何らかのメソッドをチェインする必要があるときにはどうすればよいでしょうか。ご心配なく、Object#tryでできます。

payment.client.try(:addresses).try(:first).try(:country).try(:name)

Object#tryの何が問題なのか

一見するとObject#tryはさまざまなケースを扱えそうですが、使うとどんな問題が起きるのでしょうか。

その答えは「たくさん起きる」です。多くの場合、Object#tryの最大の問題は、問題を完全に握りつぶしてしまうことであり、nilが問題である場合にも問題を「解決」してしまいます。他にも、Object#tryを使うと意図がはっきりしなくなる点も挙げられます。次のコードが何をしようとしているかおわかりでしょうか。

payment.client.try(:address)

見た目どおり、支払い(payment)にクライアントがいない場合にnilになるケースを扱っているのでしょうか。それとも、単にクライアントがたまたまnilになったときにNoMethodErrorで失敗したくないから「念のため」使っているだけなのでしょうか。もっと悪い場合を考えると、クライアントがたまたまポリモーフィック関連付けになっていて、しかもaddressesメソッドがモデルに実装されているとは限らないとしたらどうでしょう。あるいは、データ完全性にある問題が生じ、何らかの理由でクライアントが削除された支払いがいくつか発生してしまい、しかも残っていないとしたらどうでしょう。

Object#tryの使いみちの可能性があまりに多いため、上のコードを見ただけでは真の意図を知ることは不可能です。

幸いなことに、Object#tryを取り除いて明確で表現力の高いコードにできる別のさまざまなソリューションがあります。そうしたソリューションを使えば、コードのメンテナンス性と読みやすさがより高まり、バグが発生しにくくなり、二度と意図があいまいにならないようにできます。

Object#tryを使わないソリューション

Object#tryの利用状況に応じた「パターン」をいくつかご紹介します。

1. デメテルの法則を尊重する

デメテルの法則は、構造的な結合を回避するのに便利な規則です(個人的には「法則」というほどではない気がします)。要するに、仮想のオブジェクトAは自分に直接関連することにのみ関心を持つべきであり、協同または関連する相手の内部構造に立ち入るべきではないという規則です。この規則は「メソッド呼び出しのドット.は1つだけにする」と解釈されることが多いのですが、デメテルの法則は本来ドットの数の規則ではなく、オブジェクト間の結合についての規則なので、操作や変換のチェインについてはまったく問題にはなりません。たとえば次の例は法則に違反しません。

input.to_s.strip.split(" ").map(&:capitalize).join(" ")

しかし次の例は違反です。

payment.client.address

デメテルの法則を尊重することで、多くの場合明確でメンテナンス性の高いコードを得ることができます。法則に違反する十分な理由がない限り、法則を守って密結合を回避するようにすべきです。

先のpayment/client/addressを使ったコード例に戻ります。次のコードはどのようにリファクタリングできるでしょうか。

payment.client.try(:address)

最初に行うのは、構造的な結合を減らしてPayment#client_addressメソッドを実装することです。

class Payment
  def client_address
    client.try(:address)
  end
end

これでさっきよりずっとよくなりました。payment.client.try(:address)で無理やりaddressを参照するのではなく、payment.client_addressを実行するだけで済みます。Object#tryが1箇所だけになったので既に1つ改善されました。リファクタリングを続けましょう。

ここから先は2つの選択肢があります。clientがnilになるのが正当か、そうでないかです。clientがnilになるのが正しいのであれば、自信を持って明示的にnilを早期に返します(訳注: いわゆるguard構文です)。こうすることで、clientが1つもないのは有効なユースケースであることがはっきりします。

class Payment
  def client_address
    return nil if client.nil?

    client.address
  end
end

clientは決してnilになってはならないのであれば、先のguard構文をスキップできます。

class Payment
  def client_address
    client.address
  end
end

このような委譲はかなり一般的に行われます。Railsではこういう場合にうまい解決法があるでしょうか?答えは「イエス」です。ActiveSupportは、まさにこういう場合にうってつけのActiveSupport#delegateマクロを提供しています。このマクロを使えば、nilをさっきとまったく同じように扱える委譲を定義できます。

最初の例では、nilになってもよいユースケースを次のように書き換えます。

class Payment
  delegate :address, to: :client, prefix: true, allow_nil: true
end

nilになってはならない場合は次のように書き換えます。

class Payment
  delegate :address, to: :client, prefix: true
end

これで先ほどよりもずっとコードが明確になり、結合も弱まりました。Object#tryをまったく使わずにエレガントに解決するという目的を達成できたのです。

しかし、他の場所ではpaymentにclientがないことを予測しきれていない可能性がまだ残されています(完了していないトランザクションのpaymentなど)。たとえば、トランザクションが完了したpaymentのデータを表示するときになぜかNoMethodError例外でつまづいてしまうことがあります。このような場合に、必ずしもdelegateマクロでallow_nil: trueオプションが必要になるとは限りません。もちろん、Object#tryを使わなければならないということでもありません。この場合の解決法はいくつか考えられます。

2. スコープ付きデータを操作する

完了したトランザクションのpaymentを扱うときにclientが存在することを保証するなら、単に正しいデータセットを扱えるようにするのが手です。Railsアプリではこういう場合に、PaymentコレクションにActiveRecordの何らかのスコープ(with_completed_transactionsなど)を適用します。

Payment.with_completed_transactions.find_each do |payment|
  do_something_with_address(payment.client_address)
end

完了していないトランザクションのpaymentでclientのaddressを使って何かする計画はまったくないので、ここでnilを明示的に取り扱う必要はありません。

にもかかわらず、paymentの作成にclientが常に必須になっているとしても、このコードでNoMethodErrorが発生する可能性は残されています(関連付けられたclientレコードが誤って削除されてしまった場合など)。この場合は修正が必要になるでしょう。

3. データ完全性

特にPostgreSQLなどのRDBMSを使っている場合、データの完全性を確実にする方法はかなりシンプルです。ここで押さえておくべきは、テーブルの新規作成時に適切な制約を追加することです。これはデータベースレベルで行う必要があることを忘れてはなりません。モデルでのバリデーションは簡単にバイパスされてしまうことがあるため、まったく不十分です。clientnilになってしまう問題を回避するには、paymentsテーブル作成時にNOT NULL制約とFOREIGN KEY制約を追加して、clientがまったく割り当てられない状況や、関連付けが一部のpaymentに残っているclientレコードが削除されるような状況を防ぐべきです。

create_table :payments do |t|
  t.references :client, index: true, foreign_key: true, null: false
end

以上で制約の追加はオシマイです。制約の追加を忘れないようにすることで、nilで起きる予想外のユースケースの多くを回避できます。

4. 明示的な変換で型を確定する

私は次のようなかなり風変わりなObject#tryの使い方を何度か目にしたことがあります。

params[:name].try(:upcase)

このコードから、params内のnameキーから何らかの文字列が取り出せることを期待しているのが読み取れます。それならto_sメソッドで明示的に変換することで文字列型を確定させればよいのではないでしょうか。

params[:name].to_s.upcase

これで意図がわかりやすくなりました。

ただし、上の2つのコードは同等ではありません。前者はparams[:name]が文字列であれば文字列を返しますが、nilの場合にはnilを返します。後者は常に文字列を返します。場合によってnilが戻ることが期待されるかどうかは元のコードからははっきりしないので(これはObject#tryのあからさまな問題ですが)、ここでは2つの選択肢が考えられます。

  • params[:name]nilならnilを返すことが期待される場合: 文字列の代わりにnilを扱うのはかなり面倒になるので、あまりよいアイデアとはいえませんが、nilを扱う必然性がどうしても生じることもあるかもしれません。そのような場合はguard構文を追加してparams[:name]nilになる可能性があることを明示的に示す方法が考えられます。
return if params[:name].nil?

params[:name].to_s.upcase
  • 文字列を返すことが期待される場合: この場合はguard構文は不要です。先の明示的な変換をそのまま使いましょう。
params[:name].to_s.upcase

もっと複雑な状況では、Form Objectを使うか、dry-rbなどのもっと安全な型管理を導入する(あるいは両方)のがよいかもしれません。ただしこれらは明示的な型変換と本質的に同じなので、設計を損なわない限りは有用だと思います。

5. 正しいメソッドを使う

ネストしたハッシュの取り扱いは、API開発やユーザー提供のペイロードを扱うときにかなりよく見かけるユースケースです。JSONAPI互換のAPIを扱っていて、更新時にclientの名前を取得したいとしましょう。この場合は次のようなペイロードが考えられます。

{
  data: {
    id: 1,
    type: "clients",
    attributes: {
      name: "some name"
    }
  }
}

しかしAPIのユーザーが提供するペイロードが正しいかどうかがどうしてもわからない場合、ペイロードの構造が正しくないという仮定が成り立つことがあります。

こういう場合の残念な対応方法といえば、もうおわかりですね。Object#tryです。

params[:data].try(:[], :attributes).try(:[], :name)

お世辞にも美しいとは言い難いコードです。しかし面白いことに、このコードは実に簡単にきれいに書き直すことができるのです。

1つの方法は、途中のステップに明示的な変換を適用することです。

params[:data].to_h[:attributes].to_h[:name]

さっきよりよくなりましたが、もう少し表現力が欲しいところです。理想的な方法は、こういう場合のための専用メソッドを使うことです。そうした専用メソッドはいくつかありますが、たとえばHash#fetchは、指定のキーがハッシュにない場合の値も指定できます。

params.fetch(:data).fetch(:attributes, {}).fetch(:name)

これでずっとよくなりましたが、ネストしたハッシュを掘ることにもう少し特化したメソッドがあればさらによいでしょう。幸いなことに、Ruby 2.3.0からまさにこのためのHash#digメソッドが使えるようになりました。このメソッドはネストしたハッシュをくまなくチェックし、中間のキーがない場合にも例外をraiseしません。

params.dig(:data, :attributes, :name)

6. 正しいインターフェイスかダックタイピングを使う

最初に使った、必要な場合に通知を送信する例に立ち戻ります。

class MyService
  def call(object)
    object.save!
    object.try(:send_success_notification, "saved from MyService")
  end
end

このコードの改善方法は2とおりあります。

  • サービスを2つ実装する: 1つのサービスは通知を送信し、もう1つは送信しません。
class MyServiceA
  def call(object)
    object.save!
  end
end

class MyServiceB
  def call(object)
    object.save!
    object.send_success_notification("saved from MyService")
  end
end

リファクタリングしたことでコードがずっと明確になり、Object#tryも取り除けました。しかし今度は、MyServiceAを使う必要があるオブジェクトの種類とMyServiceBを使う必要があるオブジェクトの種類を知る方法が必要になります。これはこれで理解できますが、別の問題となる可能性もあります。この場合は2番目の方法がよいでしょう。

  • ダックタイピングを使う: MyServiceに渡されるすべてのオブジェクトに単にsend_success_notificationメソッドを追加します。このメソッドは何もせず、メソッドの内容は空のままにします。
class MyService
  def call(object)
    object.save!
    object.send_success_notification("saved from MyService")
  end
end

この方法なら、オブジェクトで共通する振舞いを明示的に示せるので、そうした振舞いを認識しやすくなるというメリットも得られます。元のObject#tryには多数のドメイン概念が潜んでいるため、コードの意図がわかりにくくなるという問題があります。そうしたドメイン概念が存在しないのではなく、ちゃんと認識されていないということです。Object#tryを使うとドメイン(概念)も損なわれてしまいます。これもぜひ覚えておきたい重要なポイントです。

7.「Null Object」パターン

上の通知送信の例をもう一度使うことにします。モデルの形はある程度残しつつ、少し変更しました。メソッドの引数をmailerにし、それに対してsend_success_notificationを呼びます。

class MyService
  def call(object, mailer: SomeMailer)
    object.save!
    mailer.send_success_notification(object, "saved from MyService")
  end
end

これで、必要に応じていつでも通知を送信できるようになりました。さて、通知を送信したくないときはどうすればよいでしょうか。そんなときの残念な方法といえば、mailernilを渡してObject#tryを使うことです。

class MyService
  def call(object, mailer: SomeMailer)
    object.save!
    mailer.try(:send_success_notification, object, "saved from MyService")
  end
end

Service.new.call(object, mailer: nil)

ここまでお読みになった方は、この方法を使うべきでないことがおわかりいただけると思います。ありがたいことに、Null Objectパターンを適用すれば、何もしないsend_success_notificationメソッドを実装する何らかのNullMailerのインスタンスを渡すことができます。

class NullMailer
  def send_success_notification(*)
  end
end

class MyService
  def call(object, mailer: SomeMailer)
    object.save!
    mailer.send_success_notification(object, "saved from MyService")
  end
end


MyService.new.call(object, mailer: NullMailer.new)

これでObject#tryよりずっとよいコードになりました。

ぼっち演算子&.とは何か

Ruby 2.3.0で新しく導入された&.は「ぼっち演算子」や「safe navigation operator」などと呼ばれます(訳注: 以下ぼっち演算子で統一)。ぼっち演算子は一見Object#tryとよく似ていますが、Object#tryほどあいまいではありません。nil以外のオブジェクトに対してメソッド呼び出しを行い、かつオブジェクトにそのメソッドが実装されていない場合はNoMethodErrorがraiseされます(Object#tryはそうではありません)。次の例をご覧ください。

User.first.try(:unknown_method)  # `user`がnilであるとする
=> nil

User.first&.unknown_method
=> nil

User.first.try(:unknown_method!) # `user`はnilでないとする
=> nil

User.first&.unknown_method
=> NoMethodError: undefined method `unknown_method' for #<User:0x007fb10c0fd498>

ということは、ぼっち演算子なら安全に使えるからよいのでしょうか?そうでもありません。Object#tryの重大な問題が1つ減っただけで、他の問題はそのまま変わらないからです。

しかしながら、私はぼっち演算子を使ってもよいケースが1つあると考えています。次のコード例をご覧ください。

Comment.create!(
  content: content,
  author: current_user,
  group_id: current_user&.group_id,
)

ここでは、current_userに属するコメントを1つ作成したいと考えています。current_userは作者(author)になることがあり、current_userからgroup_idを代入しますが、このgroup_idnilになる可能性があるとします。

上のコードは次のように書き直せます。

Comment.create!(content: content, author: current_user) do |c|
  c.group_id = current_user&.group_id if current_user
end

次のように書き直すこともできます。

comment_params = {
  content: content,
  author: current_user,
}

comment_params[:group_id] = current_user.group_id if current_user

Comment.create!(comment_params)

しかし、書き直したコードが、元のぼっち演算子&.を使ったサンプルより読みやすくなったとは思えません。このように、意図があいまいになるのと引き換えに読みやすさを優先したい場合には、ぼっち演算子&.が有用なこともあります。

まとめ

私は次の理由から、Object#tryの有効なユースケースはひとつもないと信じています。Object#tryを使うと意図があいまいになってしまい、ドメインモデルに負の影響が生じます。Object#tryは問題を美しくない方法で「解決」してしまいますが、同じ問題をもっとスマートに解決できる方法が「デメテルの法則の尊重と委譲」から、「スコープが正しく設定されたデータを扱う」、「データベースに正しい制約を適用する」、「明示的な変換で型を確定させる」、「正しいメソッドを使う」、「ダックタイピングを利用する」「Null Objectパターン」に至るまで数多く存在するという単純な事実があります。ぼっち演算子&.すら、用途を限定すればずっと安全に使うことができます。

訳注: 本記事では言及されていませんが、!付きのObject#try!は実質ぼっち演算子と同じに使えます。
ぼっち演算子が#try!と少し異なるのは、引数付きだとnilのときに引数が評価されないという点です。
参考: Safe Navigation Operator で呼ばれるメソッドの引数はレシーバが nilなら評価されない

関連記事

Rubyスタイルガイドを読む: クラスとモジュール(2)クラス設計・アクセサ・ダックタイピングなど

Railsの`CurrentAttributes`は有害である(翻訳)

週刊Railsウォッチ(20180316)Rails 5.2のドキュメント更新中、Value Objectの使い方、RubyがTIOBEトップテン復活、Rails「雪だるま」エンコーディングほか

$
0
0

こんにちは、hachi8833です。先週終点で車両の座席に置き忘れたiPhone 7が粉々になって戻ってきて風景がぐらりとかしいだ気がしましたが、補償が効いて本体交換できてケロッと立ち直りました。春ですねぇ。

春たけなわのウォッチ、いってみましょう。

Rails: 今週の改修

5.2はまだ出ていませんが、ドキュメント更新が増えていて、収束に近づいていることを感じさせます。今週も5.2-stableと6.0向けmasterの両方から見繕いました。いずれも変更の可能性がありますので。

Rails 5.1->5.2アップグレードドキュメント

まずは5.2-stableから。CSPとドキュメント周りの改修が目立ちます。

一応現時点のアップグレードドキュメントです↓。今のところ作業量は少なくて済みそう。

Rails 5.1からRails 5.2へのアップグレード
* Bootsnap

#29313でRails 5.2からBootsnap gemが含まれるようになりました。app:updateタスクはboot.rbで設定されます。使いたい場合はGemfileにこのgemを追加し、使わない場合はBootsnapを使わないようにboot.rbを変更してください。

  • cookie値に署名済みまたは暗号化cookieの有効期限が設定されるようになった

セキュリティ向上のため、署名済みまたは暗号化cookieの値に有効期限の情報が埋め込まれるようになりました。これによって、5.2より前のRailsとcookieバージョンの互換性が失われます。5.1以前のcookieが必要な場合や、5.2デプロイを検証中でロールバックの道を残しておきたい場合は、Rails.application.config.action_dispatch.use_authenticated_cookie_encryptionfalseに設定してください。

同コミットより大意


つっつきボイス:bootsnapはShopifyのgemがRailsで標準採用になったやつですね」「Shopfyはカナダのオタワですって」「運用中のサーバーでcookieの互換性が失われると、挙動としてはたとえば強制ログアウトが発生したりとか」「ソシャゲみたいにユーザーがめちゃ多いサービスでcookieが一斉に切れるとヤバイ: ユーザーが再ログインしようとして一気に押しかけて、ログインサーバーに負荷が集中してお亡くなりになったりとか」

その後ロードバランサーなどの話題になりました。

Railsエンジンを場所を変えてマウントできるようになった

# actionpack/lib/action_dispatch/routing/mapper.rb#L652
           def define_generate_prefix(app, name)
             _route = @set.named_routes.get name
             _routes = @set
-            app.routes.define_mounted_helper(name)
+
+            script_namer = ->(options) do
+              prefix_options = options.slice(*_route.segment_keys)
+              prefix_options[:relative_url_root] = "".freeze
+              # We must actually delete prefix segment keys to avoid passing them to next url_for.
+              _route.segment_keys.each { |k| options.delete(k) }
+              _routes.url_helpers.send("#{name}_path", prefix_options)
+            end
+
+            app.routes.define_mounted_helper(name, script_namer)
+
             app.routes.extend Module.new {
               def optimize_routes_generation?; false; end
+
               define_method :find_script_name do |options|
                 if options.key? :script_name
                   super(options)
                 else
-                  prefix_options = options.slice(*_route.segment_keys)
-                  prefix_options[:relative_url_root] = "".freeze
-                  # We must actually delete prefix segment keys to avoid passing them to next url_for.
-                  _route.segment_keys.each { |k| options.delete(k) }
-                  _routes.url_helpers.send("#{name}_path", prefix_options)
+                  script_namer.call(options)
                 end
               end
             }

これは実は昨年のコミットですが、#793c11dのコミットメッセージで目に止まったので。


つっつきボイス: 「同じマウンタブルエンジンを別名でマウントできるようになったと」「マウンタブルエンジン使うのって、Sidekiqの管理画面をマウントするときぐらいだけどなっ」「あとletter_opener導入するとエンジン入ってブラウザで見られますね」

CSPがWelcomeページやmailerプレビュー表示を邪魔しないよう修正

# railties/lib/rails/application_controller.rb#L7
+  before_action :disable_content_security_policy_nonce!
+
+  content_security_policy do |policy|
+    if policy
+      policy.script_src :unsafe_inline
+      policy.style_src :unsafe_inline
+    end
+  end
...
+    def disable_content_security_policy_nonce!
+      request.content_security_policy_nonce_generator = nil
+    end

つっつきボイス: 「こうやって追いかけているとわかりますが、最近のRailsではこういうCSP周りがちょくちょくアップデートされてますね」

CSPをコントローラからオフにできる機能を追加

# actionpack/lib/action_controller/metal/content_security_policy.rb#L16
     module ClassMethods
-      def content_security_policy(**options, &block)
+      def content_security_policy(enabled = true, **options, &block)
         before_action(options) do
           if block_given?
             policy = request.content_security_policy.clone
             yield policy
             request.content_security_policy = policy
           end
+
+          unless enabled
+            request.content_security_policy = nil
+          end
         end
       end

つっつきボイス: 「オンにできるならオフにできないとね」「たしかに」

CSPポリシーインスタンスを常にyieldするように変更

# actionpack/lib/action_controller/metal/content_security_policy.rb#L17
       def content_security_policy(enabled = true, **options, &block)
         before_action(options) do
           if block_given?
-            policy = request.content_security_policy.clone
+            policy = current_content_security_policy
             yield policy
             request.content_security_policy = policy
           end
...
+
+      def current_content_security_policy
+        request.content_security_policy.try(:clone) || ActionDispatch::ContentSecurityPolicy.new
+      end

つっつきボイス:cloneやめて常に同一のCSP設定を参照できるようにしたと: でないと挙動を追ったり変更したりできないですからね」

i18nドキュメント更新


  • 存在しなくなったGlobalize::Backend::Staticへの参照を削除
  • Google Groupsへの参照を削除
  • Globalize3への参照を削除(紛らわしいので)
  • 保存したコンテンツの翻訳方法についてのセクションを追加

本ガイドに記述されているI18n APIは、主にUI文字列の翻訳への利用を意図しています。モデルのコンテンツの翻訳手法をお探しの場合は、UI文字列とは別のソリューションが必要です。
モデルコンテンツの翻訳で役立ついろいろなgemがあります。

  • Globalize: 翻訳用の別テーブルに訳文を保存できます。1つのテーブルが1つの翻訳済みモデルになります。
  • Mobility: 訳文用テーブルやJSON columns(PostgreSQL)などさまざまな形式で訳文を保存できます。
  • Traco: Rails 3や4向けの翻訳可能なカラムを使えるようにします。カラムは元のテーブル自身に保存します。
    ガイド更新箇所より大意(強調はTechRacho編集部)

i18nのサードパーティgemがいくつか公式ガイドに載ったのが目に止まりました。


つっつきボイス: 「お、i18n gemを紹介してくれるようになるのか!」「公式が推してくれるのはうれしいっすね」「今のところGlobalizeがメジャーらしいです」「↑上にも書いてますがUI翻訳のyamlはRailsでサポートするけどコンテンツの方はRailsがサポートすることは今後もないだろうから、こういう形にしたのかも」
「モデルのi18nは自力で実装するとつらいよw: 昔やったけど当時はこういうgemなかったんで」「あ、例のMangaRebornですね」「たとえばサイトにデフォルト言語を設定したりとか、フォールバックする言語を指定したりとか必要になってくるので」「台湾語がなければ中国語、みたいな」
「Mobilityは例のshioyamaさんです↓: 名字のSalzbergをそのままもじってますね」

RubyのModule Builderパターン #1 モジュールはどのように使われてきたか(翻訳)

「こういうi18n gemは、コードを追うまではしないとしても、どういうインターフェイスを用意しているかという部分に注目して比較してみると結構勉強になりますよ: みんなそれぞれ個性があって」

ルーティングガイド更新

Railsのルーティング設定

アプリやエンジンのルーティングはconfig/routes.rbに保存されます。以下は典型的な外観です。

Rails.application.routes.draw do
  resources :brands, only: [:index, :show]
    resources :products, only: [:index, :show]
  end

  resource :basket, only: [:show, :update, :destroy]

  resolve("Basket") { route_for(:basket) }
end

これは普通のRubyソースファイルなので、Rubyのあらゆる機能を用いてルーティングを定義できますが、変数名がルーターのDSLと衝突しないようにご注意ください。
メモ: ルーティング定義を囲むRails.application.routes.draw do ... endブロックは、ルーターのDSLがスコープを確立するために必要なので絶対に削除しないでください。
ガイド更新箇所より大意(強調はTechRacho編集部)


つっつきボイス: 「Railsのルーティングの包括的というか完全なドキュメントが欲しいっすねマジで: 機能はやたらめったらあるけど、知らないと使いようのない機能の多さではRails内ではトップかも」
「今頃変数名にはご注意…だと?」「asとか使うとヘルパーが自動生成されたりとかゴロゴロありますからねー」「まRailsに慣れてくると『この語はキケン』みたいなのをだんだん身体で思い知るけど」「(´・ω・`)」

Railsのルーティングを極める(前編)

「そうそう、sheepみたいに単数形複数形が同じ語を使うと、生成されるヘルパー名が通常と違ってくることあります」「え~~!」

resources :penguins

# 通常は以下が生成される
penguin_path(@penguin)
penguins_path
resources :sheep

# 
sheep_path(@sheep)
sheep_index_path   # 区別のため「_index」が付く

「ActiveSupportにそういう活用形をチェックするメソッドがある↓」「それは知ってたけど…くぅ」「活用形といえばdataは複数形で、単数形はdatum: みんなもう知ってるよね!」

[Rails5] Active Support::Inflectorの便利な活用形メソッド群

ラテン語由来の英単語はたいてい不規則活用になりますね。symposionとsymposiumとか。ちょっと話はそれますが、indexの複数形はindicesが正式とされていますが、近年急速にすたれつつある印象です。

ActiveSupport::Cache::Entryをメモ化してマーシャリングの負荷を軽減

これは5.2-stableとmasterの両方に入っていました。ここからはmasterです。

# activesupport/lib/active_support/cache.rb#L806
+        def marshaled_value
+          @marshaled_value ||= Marshal.dump(@value)
+        end

メモ化といえば、おなじみ「縦縦イコール」ですね。


つっつきボイス: 「kazzさんが以前『たてたてイコール』って呼んでたのが可愛かったのでw」「本当は何て言うんだっけ?」「『オアイコール』?」
「ちなみに以前も話したことあるけど、Marshal.dumpはRubyのバージョンが変わると互換性が失われることがあるので、データベースにそのまま保存すると後で痛い目に遭うかもよ」「怖!」

起動メッセージの無意味な「Exiting」を除去

# railties/lib/rails/commands/server/server_command.rb#L158
           if server.serveable?
             print_boot_information(server.server, server.served_url)
-            server.start do
-              say "Exiting" unless options[:daemon]
-            end
+            after_stop_callback = -> { say "Exiting" unless options[:daemon] }
+            server.start(after_stop_callback)
           else
             say rack_server_suggestion(using)
           end

rails routes --expandedの横線をきれいにした

$ rails routes --expanded
--[ Route 1 ]------------------------------------------------------------
-------
(snip)
--[ Route 42 ]-----------------------------------------------------------
--------
(snip)
--[ Route 333 ]----------------------------------------------------------
---------
(snip)
$ rails routes --expanded
--[ Route 1 ]------------------------------------------------------------
(snip)
--[ Route 42 ]-----------------------------------------------------------
(snip)
--[ Route 333 ]----------------------------------------------------------
(snip)

つっつきボイス: 「前は横棒固定か」「IO.console.winsizeって初めて知った: これならターミナルに合わせて調整できるし」「地味だけどありがたい修正!」

+        previous_console_winsize = IO.console.winsize
+        IO.console.winsize = [0, 23]

参考: Rubyリファレンスマニュアル IO.console

rails routes -gで結果が空の場合のメッセージを修正

  • ActionDispatch::Routingのドキュメント更新
    • -gの記述を追加
    • rails routes--expandedオプションの説明を追加
  • ActionDispatch::Routing::ConsoleFormatter::Baseの導入
    • Baseを作ってSheetExpandedで継承し、コード重複を防止
      • Expandedのコンポーネントで末尾の”\n”を削除
      • Expanded#headerの戻り値を@bufferからnilに変更
    • -gのときのno_routesメッセージがよくなかったので修正
      • -cの場合のメッセージは「Display No routes were found for this controller」
      • -gの場合のメッセージは「No routes were found for this grep pattern」

PRメッセージより大意

# actionpack/lib/action_dispatch/routing.rb#L85
         def normalize_filter(filter)
-          if filter.is_a?(Hash) && filter[:controller]
+          if filter[:controller]
             { controller: /#{filter[:controller].downcase.sub(/_?controller\z/, '').sub('::', '/')}/ }
-          elsif filter
-            { controller: /#{filter}/, action: /#{filter}/, verb: /#{filter}/, name: /#{filter}/, path: /#{filter}/ }
+          elsif filter[:grep_pattern]
+            {
+              controller: /#{filter[:grep_pattern]}/,
+              action: /#{filter[:grep_pattern]}/,
+              verb: /#{filter[:grep_pattern]}/,
+              name: /#{filter[:grep_pattern]}/,
+              path: /#{filter[:grep_pattern]}/
+            }
           end
         end

つっつきボイス: 「最近自分はブラウザで/rails/info/routesで見ちゃうこと多いかなー」「このパスが割りと覚えにくいという」「/aとか打ってルーティングエラー出す方が早いっすね」「たしかに」

Rails

Railsビューをin_groups_ofでリファクタリング(RubyFlowより)

// 同記事より
%table.sponsors{width: "100%;"}
  - sponsors_by_level.levels.each do |level|
    - level.sponsors.in_groups_of(level.sponsors_per_line, false) do |group|
      %tr
        - group.each do |sponsor|
          %td{colspan: 12 / group.size, style: "text-align: center !important;"}
            = link_to sponsor.path do
              = image_tag(sponsor.logo_url, alt: sponsor.name, title: sponsor.name, style: "display: inline; float: none;")
    %tr
      %td{colspan: 12}
        %hr

つっつきボイス: 「ほっほー、in_groups_ofとな」「内部でeach_slice使ってるからこれを直接使う方が早かったかも、だそうです」「改修前のhaml、見たくないやつ…」

参考: in_groups_of
参考: Rubyリファレンス・マニュアル each_slice

RailsのシステムテストでJSエラーをキャッチする方法(RubyFlowより)

WARN: javascript warning
http://127.0.0.1:60979/assets/application.js 9457 Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user’s experience.
Got 3 failures and 1 other error from failure aggregation block “javascript errrors”:
1) http://127.0.0.1:60481/sso 10:18 Uncaught SyntaxError: Unexpected token ;
同記事より


つっつきボイス: 「テーブル使うのはどうかと思うけどまあそれはおいといて」「どうやらきれいにキャッチする方法がないからコンソールに出力してそっちで見れ、ってことみたい」「たしかに原理的に難しそう」

Railsのメール設定でdeliverdeliver_nowは使うな(Hacklinesより)

deliver_laterにしとけ、だそうです。

# 同記事より
class User
  after_update :send_email
  def send_email
    ReportMailer.update_mail(id).deliver_later
  end
end

つっつきボイス:deliver_nowは同期的なのか: じゃあ使いたくないやつですね」「deliver_laterで非同期になると、それはそれでテストで考慮しないといけない点が増えて大変になるけど: キューに入ったりメールサーバーが応答したりしてもそれだけでよしとできないとか」「キューに入ってコケたかどうか、とか」

参考: Rails API deliver_later

RailsでReduxのフォームを使うには(Awesome Rubyより)


redux-form.comより

# 同記事より
    def create
        authorize resource_plan, :create?
        command = GlobalContainer['plan.services.create_plan_command'] #Plan::CreatePlan.new
        respond_to do |format|
          format.json {
            command.call(resource_plan, params[:plan]) do |m|
              m.success do |plan|
                flash[:notice] = t('messages.created', resource_name: Plan.model_name.human)
                render json: { id: plan.id}, status: :ok, location: settings_plan_path(plan)
              end
              m.failure do |form|
                render json: {
                  status: :failure,
                  payload: { errors: form.react_errors_hash }
                }, status: 422
              end
            end
          }
        end
      end

Redux-Formでうまく書けたそうです。Redux-Formは完成途上らしいので、末尾でFinal Formというフレームワーク非依存JSフォームライブラリも紹介しています。


github.com/final-form/final-formより


つっつきボイス: 「コードの中でRepresenterというのを置いてますね」

HABTMをhas_many throughに置き換える(RubyFlowより)

# 同記事より
class PostTag < ApplicationRecord
  belongs_to :post
  belongs_to :tag
end
class Post < ApplicationRecord
  has_many :post_tags, -> { order(rank: :asc) }
  has_many :tags, through: :post_tags
end
class Tag < ApplicationRecord
  has_many :post_tags
  has_many :posts, through: :post_tags
end

つっつきボイス: 「絵に描いたようなHABTMリファクタリングですが、基本ということで」

参考: 仕事のねた: rails3でHABTMが非推奨になってる

Railsで巨大データをdedupする(Hacklinesより)

# 同記事より
# log.rb
class Log < ActiveRecord::Base
  has_many :user_logs

  def store(data)
    key = Digest::MD5.hexdigest(data)
    log = Log.find_by_checksum(key)
    if log.nil?
      log = Log.new(data: data, checksum: key)
      Log.transaction(requires_new: true) do
        begin
          log.save!
        rescue ActiveRecord::RecordNotUnique => e
          raise ActiveRecord::Rollback
        end
      end
    end
    log
  end
end

ActiveRecordリレーションをyield_selfでコンポジション可能にする(Hacklinesより)

# 同記事より
def call
  base_relation.
    joins(:care_periods).
    yield_self(&method(:care_provider_clause)).
    yield_self(&method(:hospital_clause)).
    yield_self(&method(:discharge_period_clause))
end

private

def care_provider_clause(relation)
  if params.care_provider_id.present?
    relation.where(care_periods: { care_provider_id: params.care_provider_id })
  else
    relation
  end
end
...

つっつきボイス:yield_selfってどっかで見たゾ」「あーこれだ↓」「そうそう、tapしないで書けるというのはちょっといいかも」「tapだとビックリマーク付きのwhere!になりますね: 破壊的にならないからいいだろ?っていう趣旨なのかな」「別に破壊的でもいい気はするけど」「メモリ効率とかの話を別にすれば、イミュータブルな方が望ましくはあるし、where!は基本使いたくはないので気持ちはわかる」

Ruby 2.5の`yield_self`が想像以上に何だかスゴい件について(翻訳)

RailsアプリをHerokuからAWSに移して年8万ドル以上節約した件について(Awesome Rubyより)


つっつきボイス: 「1000万近くって以前どれだけザルだったのかとw」「ちゃんと読んでないけど、HerokuとAWSの違いというより設定が大きかったんじゃ?」

search_flip: ElasticSearchクエリをチェインするクライアント(RubyFlowより)

★はまだ少ないです。


つっつきボイス: 「類似のgemがあるんではないかと思って」「ははあ、searchkickと違ってハッシュ使わずに書けるぞ↓ドヤアってことかな」

# 同リポジトリより
# elasticsearch-ruby
Comment.search(
  query: {
    query_string: {
      query: "hello world",
      default_operator: "AND"
    }
  }
)

# searchkick
Comment.search("hello world",
               where: { available: true },
               order: { id: "desc" },
               aggs: [:username])

# search_flip
CommentIndex.where(available: true)
            .search("hello world")
            .sort(id: "desc")
            .aggregate(:username)

RabbitMQはSidekiqの単なる置き換え以上のものだ(RubyFlowより)


つっつきボイス: 「熱烈にRabbitMQ推してますね: 永続性の保証とかで違ってくるみたい」「Sidekiqだって用途に合ってればとってもいいヨって最後に書いてますね」


rabbitmq.comより


sidekiq.orgより

Railsデプロイ前にこれだけはチェックしたい5項目(RubyFlowより)


  • public/404.htmlとか設定したか
  • HTTPSにしたか
  • URLでデータベース内容がお漏らししないようにしたか
  • 監視設定やったか
  • デプロイを自動化したか
# 同記事より
class User < ApplicationRecord
  has_secure_token :uuid # DBでUNIQUE indexにしておけばベスト

  def to_param
    self.uuid
  end
end

つっつきボイス: 「年バレネタですが『ウルトラ5つの誓い』を思い出しちゃって」

参考: ウルトラ5つの誓いとは (ウルトライツツノチカイとは) [単語記事] - ニコニコ大百科

RubyのValue Objectはこう使おう(RubyFlowより)

# 同リポジトリより
# Good
BigDecimal('100').to_i     # => 数値を変えずに精度だけ下げる
# Bad
Quantity.new(10, 'm').to_i # => コンテキストが失われる: Quantity#amountとする方がずっといい

# Acceptable
Dates::Period.to_activercord # => コンテキストによってはあり
# Questionable
Dates::Period.to_regexp      # => #regexpでいいんじゃね?

つっつきボイス: 「けっこうがっつり書いてあってよさそうです」「なぜGitHubリポジトリなのかはおいといて」「あのzverokさんだ↓」

Ruby: 「マジック」と呼ぶのをやめよう(翻訳)

QuickType.io: JSONを貼るとRubyやSwiftやJSのコードに変換するサイト(RubyFlowより)


同サイトより

Rubyの場合、例のdry-rbを使ってくれます。

  • 変換元


同サイトより

  • 変換先


同サイトより


つっつきボイス: 「お、これ便利かも」「Pythonが入ってないのが何となく男らしい」「逆変換もできたらいいな♡」

悪いのはRailsじゃない、Active Recordだっ(Hacklinesより)

# 同記事より
data = [
  { name: "Owner", email: "owner@example.com" },
  { name: "Employee", email: "employee@example.com" },
  ...
]

# Raw SQL
INSERT INTO users (name, email) 
VALUES ("Owner", "owner@example.com"), ("Employee", "employee@example.com")

# Sequel
  db[:users].multi_insert(data)

# ActiveRecord by #import
  User.import(data.first.keys, data.map(&:values))

# Arel
  table = Table.new(:users)
  manager = Arel::InsertManager.new
  manger.into(table)

  manager.columns = [table[:name], table[:email]]
  manager.values = manager.create_values_list(data.map(&values))
  • 結局SQL構文知らないと使えない
  • RubyによるSQL構文チェックがない
  • オブジェクト指向じゃない
  • メンテがつらい
  • モデルにビジネスロジックだのエンティティ構造だの追加アクションだのしょぼいロジック定義が山盛りになる

つっつきボイス: 「PV狙いのタイトルっぽい」「悪いとしたらビュー周りかなと思った」「この人『Arelは悪くない、Sequelいいヤツ』って言ってますけど、Arelについてはちょっとどうかなー」「うーむ」「私は最終的には生SQLが最強だとこっそり信じてますけど」「生SQLは覇者」「ORMでやってても結局SQLチェックしますしね」「文中のRecursive Common Table Expressionって何だったかな(↓)」

参考: COMMON_TABLE_EXPRESSION (TRANSACT-SQL) | Microsoft Docs

私がRailsよりHanamiが好きな理由(Ruby Weeklyより)


hanamirb.orgより

  • Repositoryパターンなところ
  • アクションがクラスであるところ
  • ビュークラスがあるところ

著者はあのRyan Biggさんです↓。

Railsの`CurrentAttributes`は有害である(翻訳)

SOLIDの原則その2: オープン/クローズの原則

# 同記事より
class UserCreateService
  def initialize(params, validator: UserValidator)
    @params = params
    @validator = validator
  end

  def call
    return false unless validator.new(params).validate
    process_user_data
  end

  attr_reader :params, :validator

  def process_user_data
    ...
  end
end

RubyMineで速攻殺している自動チェック項目3つ(Hacklinesより)

とても短い記事です。


つっつきボイス: 「Cucumberとかスペルチェックはともかく、”Double quoted string”って式展開がない場合はシングルクォートにしろっていうアレですかね?」「RuboCopちゃんに怒られるから基本シングルクォートにする癖がついてる」「実はオフにする方法を知らないけどなっ: 玉突きで変更しないといけないし」

Rubyスタイルガイドを読む: 数値、文字列、日時(日付・時刻・時間)

海外のRuby/Railsカンファレンス

Ruby/Rails関連おすすめ情報源(RubyFlowより)

書籍やサイトやチュートリアルがずらっと並んでいます。

あるRails開発会社の会社概要(RubyFlowより)

開発ツールや進め方が割りと事細かに書かれています。全部字ばっかりなのが逆に珍しいかも。

ビューやヘルパーは別世界か


つっつきボイス: 「ちょうど最近この辺の話をよくしてたので」「そうそう、#lとか#tみたいにビュー全体で使うようなのをヘルパーに置くのはまだわかるんだけど、『これ汚いからビューから逃したい』みたいなのをグローバルなヘルパーに置くのはどうかな~っていつも思ってる」「さすがヘルパー嫌いマン」「Railsのヘルパーは、WordPressで言うfunctions.phpみたいなものって説明してもらって腑に落ちたことあります」
「a_matsudaさんといえばactive_decoratorの作者ですよね: モデル名と紐付いているデコレータのモジュールをビューに行くまでにこっそりインクルードするみたいな: まさにそういう話ですよね↑」「俺それ正解だと思うよマジで」

「ヘルパーに置いてグローバルになるくらいなら、いっそコントローラに書いちゃいますね: helper_methodっていうメソッド↓があって、これを使うとコントローラにヘルパーを書けちゃうんですよ」「へー!」「知らなかった」「しょっちゅうは使わないけど、ここぞというときに控えめに使う感じで: 内部の挙動はまだよく知らないし本当にいいものかどうかはちょっと微妙なんですが」

参考: Rails API helper_method

class ApplicationController < ActionController::Base
  helper_method :current_user, :logged_in?

  def current_user
    @current_user ||= User.find_by(id: session[:user])
  end

  def logged_in?
    current_user != nil
  end
end

Railsの「雪だるまエンコーディング」問題が修正☃️

先ほど流れてきたので。

その他小粒記事

Ruby trunkより

特殊変数を排除してPathnameを高速化(継続)

Regexp#=~だと$&などの特殊変数を更新する分オーバーヘッドが生じるので、更新しないRegexp#match?に置き換えたとのことです。ベンチマークの書式がびしっと整ってます。

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


つっつきボイス: 「ちょっと話それるけど、名前がFileなのにパスっぽいものも渡されたりするとどうかと思うことがある」「それは確かによくないかも」

提案: キーがない場合にraiseする#dig!(継続)

hash = {
    :name => {
        :first => "Ariel",
        :last => "Caplan"
    }
}

hash.dig!(:name, :first) # => Ariel
hash.dig!(:name, :middle) # => nil   ●これをraiseしたい
hash.dig!(:name, :first, :foo) # raises TypeError (String does not have #dig method)

「キーワード引数でできるのでは?」「deep_fetch gemでできる」という回答です。


つっつきボイス: 「ビックリマーク付きの#dig!か」「!はこの場合いいんだろうか?」

Ruby:「プリマドンナメソッド」の臭いの警告を私が受け入れるまで(翻訳)

提案: begin(またはdo)-elseendrescueが抜けてたらsyntax errorにしたい(受理)

begin
  p :foo
else
  p :bar
end

# => :foo
# => :bar

joker10071002さんです。特にdoで始まるときにrescueを置き忘れやすいので、syntax errorにしたいとのことです。


つっつきボイス: 「自分的にはwarningのままの方がいいかなーという気がするけど」「そういえばエラー処理のelseensureってどう違うんでしたっけ?」「elseは上のどれでもなかった場合で、eusureは結果にかかわらず必ず実行するやつだったかと」

なおその後acceptされました。

参考: Rubyリファレンスマニュアル begin

Ruby

RubyにもGolangのdeferが欲しいので作ってみた話


つっつきボイス: 「ちょうど上の話にも通じてる」「Goのdeferはブロックの外にも置けてRubyのensureより強力な印象ですね: まだ使ったことないけど」「JavaScriptのPromiseもDeferredって呼ばれてた」

参考: Golang の defer 文と panic/recover 機構について - CUBE SUGAR CONTAINER
参考: 非同期処理とPromise(Deferred)を背景から理解しよう - hifive

licensed: GitHub自ら提供する依存関係のライセンス照合/キャッシュgem(Ruby Weeklyより)

# 同リポジトリより
$ bundle exec licensed status
Checking licenses for 3 dependencies

Warnings:

.licenses/rubygem/bundler.txt:
  - license needs reviewed: mit.

.licenses/rubygem/licensee.txt:
  - cached license data missing

.licenses/bower/jquery.txt:
  - license needs reviewed: mit.
  - cached license data out of date

3 dependencies checked, 3 warnings found.

まだ1か月経ってない新しいgemです。類似のgemを取り上げたことがありました。

rakeタスクをきれいに書くコツ(Hacklinesより)

DHHのYouTubeチャンネルでヒントを得たそうです。

asciidoctor: AsciiDoc形式テキストプロセッサのRuby版(Awesome Rubyより)

= Hello, AsciiDoc!
Doc Writer <doc@example.com>

An introduction to http://asciidoc.org[AsciiDoc].

== First Section

* item 1
* item 2

[source,ruby]
puts "Hello, World!"

AsciiDocはこんな感じ↑で書けるようです。単なるMarkdownの置き換えではないと言ってます。

参考: What is AsciiDoc? Why do we need it? | Asciidoctor
参考: 脱Word、脱Markdown、asciidocでドキュメント作成する際のアレコレ

ヒアドキュメントで式展開#{}を展開させない方法(Hacklinesより)

# 同記事より
venue = "world"
str = <<-'EOF'
I'm the master of the #{venue} !
No you're dead bro..\n
EOF

2018年のRuby GUI開発事情(RubyFlowより)


saveriomiroddi.github.ioより

なお著者はGobyのcontributorであることを今思い出しました。

RubyのリゾルバでSSRFフィルタをバイパスされる脆弱性(Hacklinesより)

# 同記事より
irb(main):008:0> Resolv.getaddresses("127.0.0.1")
=> ["127.0.0.1"]
irb(main):009:0> Resolv.getaddresses("localhost")
=> ["127.0.0.1"]
irb(main):010:0> Resolv.getaddresses("127.000.000.1")
=> [] # 😱

参考: Rubyリファレンスマニュアル Resolv

SCSSコンパイラを自力で書いてみたお(RubyFlowより)


同記事より

RubyがTIOBEのトップ10言語に返り咲く(Hacklinesより)


tiobe.comより


つっつきボイス: 「記事のグラフ見ると、むしろC言語がいったん下がってからガッと上がっているのが気になる」「JavaとC以外はまだ混戦かなー」

Aaron Pattersonさんから

#kind_ofが悪手になる場合

どこかで「#is_a?は今は非推奨」とmatzがツイートしていた気がしましたが、そのエイリアスである#kind_of?もどうやらあまり使って欲しくない様子です。


つっつきボイス: 「Matzが#kind_of?警察やってる」「#is_a?が非推奨になったのは、確か名前がよくなかったからだったような」「『純粋なオブジェクト指向ならメッセージベースでやろうぜ』『クラスなんてものはオブジェクト間の通信には本来不要である』という趣旨なんでしょうね」
「質問者の方もそうだけど、Javaから来ると型チェックしたくなる気持ちはわかる」「Javaにはインターフェイスがあるから」「Rubyにはrespond_to?がある」

私もつい型チェック的思考に傾きかけてたかも。反省。

参考: RubyリファレンスマニュアルObject#respond_to?


「ところでRailsには?なしのrespondo_toというのがあってですね」「紛らわし!」「Railsを先にやると、Rubyのrespond_to?の方でむしろ首を傾げたりとか」「RSpecのrespond_toマッチャーも同じ過ぎるし」「名前がこれだけ似てて意味がまるで違うという」

参考: Rails API respond_to

Ruby生誕25周年記念: コミットのビジュアル表示

SQL

データベースのモデル化アンチパターン3種(Postgres Weeklyより)


  1. Entity Attribute Values
  2. Multiple Values per Column
  3. UUID

つっつきボイス: 「EAVはSQLアンチパターンにも載っている定番中の定番っすね: 一度はやりたくなってしまうやつ」「略語になってるんですね」
「むかーしエンタープライズ系のJavaの本で『EAVはベストプラクティスのひとつである』みたいな記述があったんですが」「マジかーw」「いや、たぶんこれはメモリ構造に乗せてEAVする分にはよかったはず: RDBMSでやるもんじゃないですよもちろん」「後で検索が必要になったときに死ねるやつ」
「2.は今のRDBMSなら普通にできたりしますね: PostgreSQLのArrayとか」

PostgreSQLの全文検索でVACUUMを使うときにやるべきこと(Postgres Weeklyより)


つっつきボイス: 「出たーVACUUM ANALYZE: めちゃめちゃ重い」

PostgreSQLの新機能「シーケンス」のメリットと落とし穴(Postgres Weeklyより)

動画: データベースの隠し技紹介(Postgres Weeklyより)

オーストラリアでこの3月に行われたRubyカンファレンスであるRubyConf Auでの発表です。

JavaScript

prettier: JS界のRuboCop


同リポジトリよりより

★めちゃ多いです。

JavaScriptのエレガントな「ROROパターン」(Frontend Weeklyより)

割りと長い記事です。ROROは「Receive an object, return an object」だそうです。RubyのPOROとは違いました。

スーパー速い「Radi.js」フレームワークを作ったお(JavaScript Weeklyより)

Virtual DOMを使わないことで速くしたそうです。

Glimmer.jsとPreact.jsのパフォーマンス比較(JSer.infoより)

Linkedinの技術ブログです。

CSS/HTML/フロントエンド

Tumult Hype: フロントのアニメーション表示を徹底制御(Hacklinesより)

<a https://tumult.com/hype/”>
同記事より

HoudiniプロジェクトのCSS Paint API(Frontend Focusより)

Chrome 65以降でないと動かないので、brew cuで速攻Chromeをアップグレードしました。以下で「Fail」が出るブラウザではできないそうです。

See the Pen CSS Paint API Detection by Will Boyd (@lonekorean) on CodePen.

動画: Chromeの新機能「Local Overrides」でパフォーマンス上の仮説をテストする(Frontend Focusより)

「King’s Pawn Game」に学ぶUIデザイン(Frontend Weeklyより)


同記事より

UIデザイナー向けの記事です。King’s Pawn Gameは、チェスの序盤の定石のようです。

参考: Wikipedia-en King’s Pawn Game

CSSで四隅を切り欠くには(Frontend Focusより)

See the Pen Notched Boxes by Chris Coyier (@chriscoyier) on CodePen.

World Wide Webが29歳の誕生日(Frontend Focusより)

その他

Stackoverflowのアンケート結果

かなり長いです。

MacのiTerm2で出力を任意のエディタに送り込む(Hacklinesより)

AppleScriptの小ネタです。

若手開発者サバイバルガイド: コードが動かないときにうまく先輩に伝えるには(Hacklinesより)


つっつきボイス: 「これも翻訳打診してみますね」

HomebrewとPythonバージョンの混乱

Mathpix snipping tool: 数式を撮影するとLaTeXに変換するスマホアプリ

よく見たら昨年からiPhoneにインストールしてました。どっちかというとソルバーです。

番外

「いいこと聞いた」と思うかどうかが分かれ目?

ソイレントといえば

私の年だとソイレント・グリーンですが、ゼノギアスの方が有名っぽいですね。

たけのこ

巨星墜つ

モンティ・パイソンに出演したホーキング博士も素敵でした。


今週は以上です。

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

週刊Railsウォッチ(20180309)RubyGems.orgのTLS 1.0/1.1接続非推奨、2年に1度のRailsアンケート、DockerのMoby Project、Ruby拡張をRustで書けるruruほか

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

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

Ruby 公式ニュース

Rails公式ニュース

Ruby Weekly

Awesome Ruby

RubyFlow

160928_1638_XvIP4h

Hacklines

Hacklines

Postgres Weekly

postgres_weekly_banner

Frontend Weekly

frontendweekly_banner_captured

Frontend Focus

frontendfocus_banner_captured

JavaScript Weekly

javascriptweekly_logo_captured

JSer.info

jser.info_logo_captured

週刊Railsウォッチ(20180323)Rails 5.2.0 RC2リリース、「サーバーレスなRubyが欲しい」、capybara風JSテストフレームワークCypressほか

$
0
0

こんにちは、hachi8833です。明日のRails Developers Meetup 2018準備で青ざめてます。

今週から記事数をできるだけ一定に保つようにいたしました。腹八分目のウォッチ、いってみましょう。

Rails: 今週の改修

Rails 5.2.0 RC2リリース

5.2.0はほぼ仕上がったそうです。

大きな変更はTechRachoでもおおよそ取り上げたので、5.2 RC2リリースノートの中からこれまで取り上げていなかったコミットを中心に見ていきます。

capify!の非推奨化

Capistrano 3でコマンドがcap installに変わったことへの対応です。!なしは元々なかったんですね。

# railties/lib/rails/generators/actions.rb#L229
      def capify!
+        ActiveSupport::Deprecation.warn("`capify!` is deprecated and will be removed in the next version of Rails.")
         log :capify, ""
         in_root { run("#{extify(:capify)} .", verbose: false) }
       end

つっつきボイス: 「そうそう、昔のCapistranoではcapファイルの作成とか設定とかデプロイに使うコマンドがcapifyでしたね」「あれ、CapistranoってRails標準だったかな?」「あ、そういえば: と思ったらGemfileではデフォルトでコメントアウトされて↓るからオプション扱いですね」

# Use Capistrano for deployment
# gem 'capistrano-rails', group: :development

「ところでCapistranoって最近それほど見かけない気がしてましたが」「いやいや、もう当たり前に使われまくっててことさら話題になってないだけ: capファイルは普通にプロジェクトにあるし」「昔はCapistranoの設定方法がよくわからんという声もちょくちょくあったりしましたが、今もう聞かないっすね」

参考: Generators::Actions#capify!

config.ruの古い挙動のサポートを非推奨化

Rails 4以降で生成されるconfig.ruではデフォルトでRails.applicationのインスタンスが使われるようになってからだいぶ経ったことで非推奨化されました。

# railties/lib/rails/commands/server/server_command.rb#L21
-    # TODO: this is no longer required but we keep it for the moment to support older config.ru files.
     def app
       @app ||= begin
         app = super
+        if app.is_a?(Class)
+          ActiveSupport::Deprecation.warn(<<-MSG.squish)
+            Use `Rails::Application` subclass to start the server is deprecated and will be removed in Rails 6.0.
+            Please change `run #{app}` to `run Rails.application` in config.ru.
+          MSG
+        end
         app.respond_to?(:to_app) ? app.to_app : app
       end
     end

つっつきボイス: 「これもむかーしに、Rails起動時にアプリ名じゃなくてRails.applicationを指定できるように変わったので、その絡みでしょうね: Rails 4からだったかな」

後で探してみると、2013年の#9669↓がそれのようです。

# railties/lib/rails/generators/rails/app/templates/config.ru
 # This file is used by Rack-based servers to start the application.

 require ::File.expand_path('../config/environment',  FILE)
-run <%= app_const %>
+run Rails.application

rails runnerの引数で-をサポート

# railties/lib/rails/commands/runner/runner_command.rb#L15
       def self.banner(*)
-        "#{super} [<'Some.ruby(code)'> | <filename.rb>]"
+        "#{super} [<'Some.ruby(code)'> | <filename.rb> | -]"
       end

       def perform(code_or_file = nil, *command_argv)
# @@ -29,7 +29,9 @@ def perform(code_or_file = nil, *command_argv)

         ARGV.replace(command_argv)

-        if File.exist?(code_or_file)
+        if code_or_file == "-"
+          eval($stdin.read, binding, "stdin")
+        elsif File.exist?(code_or_file)
           $0 = code_or_file
           Kernel.load code_or_file
         else

つっつきボイス: 「ははぁ、-を指定することで標準入力が使えると: -自体は標準入力と標準出力のどっちにでも使われうるけどここでは標準入力ですね」

Relationに対するArelメソッド呼び出しが非推奨化

kamipoさんのPRです。

# activerecord/test/cases/relation/delegation_test.rb#L24
+  module DeprecatedArelDelegationTests
+    AREL_METHODS = [
+      :with, :orders, :froms, :project, :projections, :taken, :constraints, :exists, :locked, :where_sql,
+      :ast, :source, :join_sources, :to_dot, :bind_values, :create_insert, :create_true, :create_false
+    ]
+
+    def test_deprecate_arel_delegation
+      AREL_METHODS.each do |method|
+        assert_deprecated { target.public_send(method) }
+      end
+    end
+  end

つっつきボイス: 「今まではAR::Relationに対して直接arelを呼ぶと委譲されてたのが、今後は呼べなくなる方向になるってことか」「非推奨になるArelメソッドのexists↑は?なしの方なんですね: それなら直接呼べなくなってもいいかな」「fromsとかbind_valuesあたりは、ひょっとすると?使うことがあるかもしれないですが」「自分はArel直接呼ぶような事態になったら生SQL書くから別にいいや」

参考: exists?

「つまるところ、Arelは本当はprivateなAPIなんだぞってことを示してるんですかね」「Arelをどうしても使うならArelだけで組み立てて欲しい: AR::Relationで中途半端に組み立てたクエリをArelでいじるのとかやめて欲しいわー」「それで発生するバグつらそうですね…」「AR::Relationでスコープ変えたりするとまたわけわからなくなるし」

「kamipoさんのコミットメッセージ↓にこう書かれているから、relation.arel.#{method}はあっても基本使うなよってことですね」「privateなんだから代わりに呼ぶんじゃないよと」「もう禁止w」「それだけ事故が多かったからですかね」「というよりArel混ぜられると死ぬほど読みにくい!に尽きる」

I removed “Use relation.arel.#{method} instead.” in the message because it’s private API and fixed the next version of Rails to 6.0.
同PRより

ネストしたトランザクションの親のステートをベースにレコードのステートを適用

# active_record/connection_adapters/abstract/transaction.rb#L133
     class SavepointTransaction < Transaction
-      def initialize(connection, savepoint_name, options, *args)
+      def initialize(connection, savepoint_name, parent_transaction, options, *args)
         super(connection, options, *args)
+
+        parent_transaction.state.add_child(@state)
+
         if options[:isolation]
           raise ActiveRecord::TransactionIsolationError, "cannot set transaction isolation in a nested transaction"
         end

つっつきボイス: 「ここまで頑張ってネステッドトランザクション書いたことないなー: そもそもどう処理されるか不安だし」「ただトランザクションって意図せずにネストすることがありうるから、最終的に挙動は知っておいたほうがいいけど」
「そもそも生SQLでもネステッドトランザクション書いたことないしw」「ストアドプロシージャとか、使い所はなくもないですけど、普通の業務で使うかなー?と私も思いますね」「できるのはわかるけど下手するとデッドロックしかねないし」

#0237da2のコミットメッセージを雑に訳してみました:


ネストしたトランザクションがあってレコードが両方でsaveされたとします。外側のトランザクションがcloseするとロールバックします。従来は、外側のトランザクションが「not persisted」とマーキングされていても内側のトランザクションが「persisted」になることがありました。

Post.transaction do
  post_one.save # ロールバックされる

  Post.transaction(requires_new: true) do
    post_two.save # 誤ってpersistedになる
  end

  raise ActiveRecord::Rollback
end

修正のため、このPRではトランザクションの扱いを変更して、子のトランザクションが親にマーキングの状態を確認するようにします。子トランザクションがあると、スタックが空でないため常にSavpointTransactionになります。その状態からparent_transactionを子のSavepointTransaction(親への子の追加はここで行われる)に渡すと、親は内側のトランザクションを「rolledback」とマーキングするのでレコードを「not persisted」とマーキングします。

update_attributes_from_transaction_stateでは、すべてのトランザクションがrolledbackとマーキングされ、内側のレコードがnot persistedと正しくマーキングされるよう、completed?チェックを用いる。

Post.transaction do
  post_one.save # ロールバックされる

  Post.transaction(requires_new: true) do
    post_two.save # 新しい振る舞いではnot persistedと正しくマーキングされる
    on rollback
  end

  raise ActiveRecord::Rollback
end

(同commitのテストを見ながら)「miniTestに#refute_predicateってありますね」「refute: 論破する、だから否定のアサーションか」

# activerecord/test/cases/transactions_test.rb#L372
...
+    refute_predicate topic_one, :persisted?
+    refute_predicate topic_two, :persisted?
+    refute_predicate topic_three, :persisted?
...

参考: refute_predicate

Rails

ActiveRecordをfixtureに変換する(RubyFlowより)

とても短い記事です。


つっつきボイス: 「おーこれあると便利なやつ: これは.to_yaml一発だけど他にもやり方は色々ある」
「マスターデータ的なやつならこんな感じでざっとfixtureにするのはラクでいいですよ」

「fixtureを使う場合問題になるのは外部キーで、ロードするときに面倒くさい」「あー順番に依存しているからか: それは面倒くさいっすね」「yamlってだけで読みづらいのに」
「その点FactoryBotで書けば順番とかよしなにやってくれますからね: fixtureだとそれがない」
「裏技的にMySQLの外部キー制約を一時的にオフにするオプションとかあるけど、そういうことをやり始めると知らないうちにfixtureがひっそりと壊れてたり」「なんやかんやで、fixtureで何でもやろうとするのは無理ありますね」

テスティングアンチパターン: セットアップしたデータの漏れ(Hacklinesより)

# 同記事より: 悪例
describe User do
  let(:user) { create(:user) }
  let(:account) { create(:account, user: user) }
  let(:role) { create(:role, user: user) }
  let(:post) { create(:post, user: user) }

  describe 'account-related stuff' do
    # tests go here
  end
  ... 
end

つっつきボイス:letを全部外側に置くとそのスコープ全部に効いちゃうからやめろよっていう、しごく普通のハナシ」「let嫌いーw: 遅延評価されるところとか」「お、let嫌いの同志を発見!」「let好き勢も社内にいますからねー」「letはキレイに使えるとかっこいいんだけど」「テストの内容によってはランダマイザーの影響とかで数回に1回とか数十回にコケたりすることもあるし」「途中でreloadする羽目になって面倒くさくなるし、reload使うと何だか負けな気がしたり」「基本letで定義したものは参照だけにしてテストの中で変えない方がいい

モデル内の翻訳する文章をJSON化する(Hacklinesより)


同記事より


つっつきボイス: 「これもローカライズ絡みということで」「まあ普通に行われていることですね: mangareborn.jpではyamlでやってたし」「データがでかくなると大変ですが」

loofah: nokogiriベースのHTML/XML操作/サニタイズgem

# 同リポジトリより
span2div = Loofah::Scrubber.new do |node|
  node.name = "div" if node.name == "span"
end

doc = Loofah.fragment("<h1>Title</h1><div>Content</div>")
doc.text    # => "TitleContent"           # probably not what you want
doc.to_text # => "\nTitle\n\nContent\n"   # better

つっつきボイス: 「ほー、HTMLを食わせて要素を置き換えたりto_textしたりサニタイズできる: XPathも使えるみたいだし、うんなかなかよさそうです」「ちなみにこのgemは脆弱性情報↓で知りました」「この種のパーサーはともすると脆弱性が潜むことがあるといえばありますからね」

JetBrains IDEはVMを設定しよう


つっつきボイス: 「JetBrainsにかぎらず、巨大IDEはEclipseでも何でもVMを適切に設定しておけばそう重くなったりしないですよ」

morimorihogeさんのRubyMineでは少なくとも以下ぐらいにVMを設定しているそうです。私のはデフォルトのままだった…

-Xms1024m
-Xmx8192m

ViewModelを分けることについて


つっつきボイス: 「TechRacho記事を引用いただいていたので見つけました」「ViewModelとPresenterを分けるっていう話のようですね」「ViewModelって?」「いわゆるMVVMパターン: JavaScriptの方でよく使われてる」「確かにPresenterが分かれている方がテストはしやすいですね」「正しいモデルを作りにくいけどビューの見た目だけチェックしたいときとか、ViewModelだけ差し替えられるようになってるとテストしやすい」「そういえばこの間の社内勉強会で近年のAndroidの動向を扱ったときもこの辺りの話が出ましたね」

参考: Wikipedia-ja MVVM

GitLabで重大なセキュリティリリース(Hacklinesより)


つっつきボイス: 「おっと、社内のGitLabもアップデートするか」

Ruby trunkより

ヒアドキュメントで改行をエスケープしたときの問題

puts <<~TXT.inspect
  1 \
  2
TXT

# 期待:
"1 2\n"

# 実際:
"1   2\n"

つっつきボイス: 「Rubyのヒアドキュメント記法って6つぐらいなかったっけ?覚えるの大変」「この<<~をSQLクエリで使うと先頭のスペースがなくなるからログが読みやすくなるって確かTechRachoの記事で見ましたよ」「(う、どこだったかな…記号検索難しい…)」「Rubyではこういうヒアドキュメントが言語仕様として用意されているあたり、ワカッテラッシャル」

参考: Rubyリファレンスマニュアル: ヒアドキュメント

誤った代入でsegfaultする->#14261で修正済みだった

# 同issueより
def foo
  puts 'hi'
end

foo, true  # 落ちる

つっつきボイス: 「むー、trueの前にカンマがあるからブロックと勘違いされたわけではなさそうだけど」「パーサーの気持ちになるのは難しす…」

CLI向けに文字が全角か半角かを取れるメソッドが欲しい

他の言語やgemにもある↓から言語にも取り入れて欲しいということのようです。

Python: unicodedata.east_asian_width (standard library)
https://docs.python.org/3.6/library/unicodedata.html#unicodedata.east_asian_width

Perl: "East_Asian_Width: *" of Unicode properties (regular expression in language)
https://perldoc.perl.org/perluniprops.html

Go: golang.org/x/text/width
https://godoc.org/golang.org/x/text/width

PHP: mb_strwidth (standard library)
http://php.net/manual/en/function.mb-strwidth.php

JavaScript: eastasianwidth (npm library)
https://www.npmjs.com/package/eastasianwidth

RubyGems: unicode-display_width gem
https://rubygems.org/gems/unicode-display_width

つっつきボイス: 「Unicodeの文字幅って全角と半角しかないんだっけ?」「んー、合字とかを別にすればそうだったかもしれないけど、どうだったかな…」

後で取り急ぎ以下を見つけました。

参考: [Ruby] Unicode 文字列の幅をそれなりにがんばって取得する - あおたくノート

プロポーショナルフォントの場合どうしようもないんだけど Unicode の文字は EastAsianWidth という仕様があって文字毎の幅がいわゆる全角か半角のどちらかになるかが決められている。
決められているといっても一部の文字は「Ambiguous(決定できない)」というふうに決められているのでそういう文字は自分で全角に倒すか半角に倒すか選ぶ必要はある。
同記事より

issueでも言及されているjanlelis/unicode-display_widthで「一応」取れるようです。

Ruby

「サーバーレスなRubyが欲しい」署名運動(Ruby Weeklyより)


つっつきボイス: 「サーバーレスRubyね…、あったらうれしいけどRailsが動くわけじゃないだろうし」「ここで署名している人の半分ぐらい、もしかしてRailsを動かしたいんじゃ?」「AWSのLamdbaでRubyがまともにサーバーレスで動くようになってごく軽いマイクロサービスとか建てられるようになったら確かにうれしい」「LambdaではRubyはまだちゃんと使えないんでしたっけ?」「このサイトのファビコンにLambdaのアイコンがあるぐらいだからまだっぽいですね」「確かまだだったと思う」「JRubyは動くみたいですけどJavaが動くから当然動くし」
「本格的なサーバーレスRubyはまだ難しいんじゃないですかね: gemはどこまで使える?とか、ActiveRecordのこの機能だけ使いたいとか」「ネイティブのgemぐらいなら使えるかもしれないけど」

参考: QuickStart Guide to Using the AWS SDK for Ruby - AWS SDK for Ruby

bundler-stats: gemの依存関係を調べるgem(Ruby Weeklyより)

# 同リポジトリより
> bundle-stats

+------------------------------|-----------------|-----------------+
| Name                         | Total Deps      | 1st Level Deps  |
+------------------------------|-----------------|-----------------+
... omitted stuff here ...
| fog                          | 15              | 6               |
| fancybox2-rails              | 15              | 1               |
| quiet_assets                 | 15              | 1               |
| coffee-rails                 | 18              | 2               |
| angular-rails-templates      | 19              | 3               |
| devise                       | 19              | 6               |
| rspec-rails                  | 20              | 7               |
| sass-rails                   | 21              | 4               |
| foundation-icons-sass-rails  | 22              | 2               |
| rails                        | 29              | 9               |
| angular_rails_csrf           | 30              | 1               |
| ngannotate-rails             | 31              | 2               |
| activeadmin                  | 48              | 12              |
+------------------------------|-----------------|-----------------+

Declared Gems:     35
Total Gems:        113

Unpinned Versions: 30
Github Refs:       1

つっつきボイス: 「こういう依存関係の情報取れるのはちょっとありがたいかも」

Ducalis: RuboCopベースの静的コードアナライザ(RubyFlowより)

# 同リポジトリより
ducalis --ci --repo="author/repo" --id=3575 --dry
ducalis --ci --repo="author/repo" --id=3575
ducalis --ci --adapter=circle # mode for running on CircleCI

参考: Wikipedia-ja 静的コード解析

もう何回言われたか覚えてない「Railsは死んだか: 2018年版」


つっつきボイス: 「2018年版ってのがウケる」「恒例の行事」

Ruby 2.6 preview 1

Noah Gibbsさんの記事とツイッターのやりとりです。

その他の記事/リポジトリ

SQL

pg_badplan: クエリプランと実際のクエリの乖離をチェック(Postgres Weeklyより)

-- 同記事より
\copy zip_codes from ~/src/create-statistics-talk/no_postal_codes_utf.csv with csv header;
COPY 4574

EXPLAIN (ANALYZE, TIMING off)
SELECT * FROM zip_codes WHERE city = 'Oslo' AND county = 'Oslo';
                                      QUERY PLAN                                       
---------------------------------------------------------------------------------------
 Seq Scan on zip_codes  (cost=0.00..108.61 rows=90 width=36) (actual rows=642 loops=1)
   Filter: ((city = 'Oslo'::text) AND (county = 'Oslo'::text))
   Rows Removed by Filter: 3932
 Planning time: 0.357 ms
 Execution time: 0.679 ms
(5 rows)

データベース制約は最後の砦(Postgres Weeklyより)

CitusDataによる平易な解説記事です。

PostgreSQLで行の重複をサーチアンドデストロイする(Postgres Weeklyより)

-- 同記事より
SELECT id, firstname, lastname, startdate, position FROM
  (SELECT id, firstname, lastname, startdate, position,
     ROW_NUMBER() OVER 
(PARTITION BY (firstname, lastname) ORDER BY startdate DESC) rn
   FROM people
  ) tmp WHERE rn = 1;

JavaScript

JSプログラマーはどこで一番よく間違えるか(Frontend Weeklyより)


つっつきボイス: 「single biggest mistakeとある割に記事長いなー: 今読んでられない」「翻訳してみようかしら」

Cypress: フロントエンド向けテスティングフレームワーク


同リポジトリより

Railsでもbin/rake db:purge && cypress openで使えるそうです。


つっつきボイス: 「これはこれは、とってもCapybaraみたい↓」

describe("main navigation", () => {
  it("can navigate", () => {
    cy.visit("/");
    cy.get("[data-cy='nav-blog']").click();
    cy.location("pathname").should("eq", "/blog");

    cy.viewport("iphone-6");
    cy.get("[data-cy='nav-blog']").click();
    cy.location("pathname").should("eq", "/blog");
    cy.get("[data-cy='nav-about']").click();
    cy.location("pathname").should("eq", "/about");
  });
});

サードパーティJavaScript読み込みの影響(Frontend Weeklyより)


同記事より

Googleの解説記事です。

CSS/HTML/フロントエンド

CloudFlare Workers APIでJavaScriptを高速実行(JavaScript Weeklyより)

JavaScript向けの高速なCloudFlare Workers APIがでリリースされたという情報です。ドキュメントによるとHTTPトラフィックを扱う「Cloudflare Service Workers」とは別物だそうです。

センサーAPIがW3CでCRに(Frontend Focusより)


つっつきボイス: 「加速度計、ジャイロスコープ、磁気センサ、方位センサに間接光センサ…」

Wrapparizer: サイトで使われているソフトウェアを簡単に表示


つっつきボイス: 「既存システムがある案件の見積フェーズで有用そう」「Railsは50%で推測してますね」「JavaScriptの方をチェックしているのかも」

その他

Java 10がリリース


同記事より

参考: Java 10が本日付で正式リリース。ローカル変数の型推論、ガベージコレクタが入れ替え可能、不揮発性メモリ対応など。Java 9は早くもサポート期間終了 - Publickey

参考: Java 10新機能まとめ - Qiita

日本で働く外人エンジニア

読み物記事です。日本語混じりなので翻訳が逆に難しそう。


つっつきボイス: 「これはいろいろ面白いー」「忖度忖度」「『現在和訳中です』か…」「↓これもいいですね」

参考: How To Write Letters In Japanese

その手があった

番外

奥村先生が受賞

おめでとうございます。

塩粒以下のコンピュータ

アリさんに埋め込んだりできるでしょうか。


つっつきボイス: 「もう花粉症引き起こせますね」「こういうのが互いに相互連携して動くみたいなやつも出始めてるらしい」「体内で動くのはちょっと怖いかも」

参考: IBMが世界最小のコンピューターを発表、塩の粒より小さいサイズ - GIGAZINE

ブレインスキャナーがウェアラブルに

ポイントは「地磁気の影響をどうやって遮断するか」だったそうです。


今週は以上です。明日のRails Developers Meetup 2018でお会いしましょう。スライドがんばる…

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

週刊Railsウォッチ(20180316)Rails 5.2のドキュメント更新中、Value Objectの使い方、RubyがTIOBEトップテン復活、Rails「雪だるま」エンコーディングほか

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

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

Rails公式ニュース

Ruby Weekly

Awesome Ruby

RubyFlow

160928_1638_XvIP4h

Hacklines

Hacklines

Postgres Weekly

postgres_weekly_banner

Frontend Weekly

frontendweekly_banner_captured

Frontend Focus

frontendfocus_banner_captured

JavaScript Weekly

javascriptweekly_logo_captured

JavaScript Live

jslive_logo_captured

JSer.info

jser.info_logo_captured

週刊Railsウォッチ(20180330)春のリリースラッシュ: Rails 5.1.6/5.0.7とRuby 2.5.1など、Ruby 2.2は3月でメンテ終了ほか

$
0
0

こんにちは、hachi8833です。Rails Developers Meetup 2018が無事終了しましたね。終わった後は久々のスタミナゼロ状態でした。関係者の皆さま、本当にありがとうございました。

年度末の忙しさの中、桜咲く3月最後のウォッチ、いってみましょう。
今回からh2タグとh3タグにアンカーを付けました。

⚓臨時ニュース

⚓Rails 5.1.6と5.0.7リリース

Rails 5.2.0より先に既存の更新がリリースされました。railtiesのChangelogを見た感じでは、後述のRuby 2.2.10リリースを受けて出されたという一面もあるようです。


rubyonrails.orgより

⚓Ruby 2.5.1リリース: バグ/セキュリティ修正中心(Ruby公式ニュースより)


ruby-lang.orgより

本体および標準ライブラリのバグやセキュリティ修正です。過去のバージョンにもバックポートされました。

記念写真というより証拠写真でしょうか。

中の人のブログ↓も公開されています。


つっつきボイス: 「webrickの脆弱性などなど」「webrickを本番で使う人はまずいないでしょうねー」「確かに」

いるのかな?

参考: ruby/webrick

#unpackって例のC言語のprintfみたいないやらしーやつでしたっけ」「16進まみれのw」「pack/unpackこの間しぶしぶ使ったけど、@指定できるとか知らんわー」

参考: Rubyリファレンスマニュアル String#unpack

⚓Ruby 2.2系は3月いっぱいでメンテナンス期間終了

同じプレスリリースです。極めて重要なセキュリティ修正を除き、今後2.2は更新されなくなります。


つっつきボイス: 「Rubyは毎年リリースされているから終了も毎年あるわけですね: 3月はさよならのシーズンというか」「サポートなくても動くは動くけどなっ」「2.2以下のRuby、あのアプリとかこのアプリとかあったかなー」「それより先にRails 5にアップデートせいという感じか」「Rubyをアップグレードする方が楽かな」

⚓Rails: 今週の改修

今回の公式情報はまとめ的な感じです。masterなので基本的にRails 6向けですね。
公式で紹介されている今回のコミットログはいつになくあっさりしているので、気軽に読めますね。

⚓Active StorageでAWS S3 SDK認証オプションを完全サポート

# activestorage/lib/active_storage/service/s3_service.rb#L9
   class Service::S3Service < Service
     attr_reader :client, :bucket, :upload_options

-    def initialize(access_key_id:, secret_access_key:, region:, bucket:, upload: {}, **options)
-      @client = Aws::S3::Resource.new(access_key_id: access_key_id, secret_access_key: secret_access_key, region: region, **options)
+    def initialize(bucket:, upload: {}, **options)
+      @client = Aws::S3::Resource.new(**options)
       @bucket = @client.bucket(bucket)

       @upload_options = upload

つっつきボイス:**optionsで素直にまるっと渡せるようになったってことか」「渡しても全然大丈夫だったと」「パラメータで{}**が隣り合っていると一瞬ドキっとしちゃいます: どっちに落っこちるんだみたいな」「そんなんばっかしですよもうw」

⚓MySQL2のサポート

# activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb#L6
-gem "mysql2", "~> 0.4.4"
+gem "mysql2", ">= 0.4.4", "< 0.6.0"

バージョン書き換えただけです。

⚓i18nの@virtual_path置換え結果をメモ化

# actionview/lib/action_view/helpers/translation_helper.rb#L124
       private
         def scope_key_by_partial(key)
-          if key.to_s.first == "."
+          stringified_key = key.to_s
+          if stringified_key.first == "."
             if @virtual_path
-              @virtual_path.gsub(%r{/_?}, ".") + key.to_s
+              @_scope_key_by_partial_cache ||= {}
+              @_scope_key_by_partial_cache[@virtual_path] ||= @virtual_path.gsub(%r{/_?}, ".")
+              "#{@_scope_key_by_partial_cache[@virtual_path]}#{stringified_key}"
             else
               raise "Cannot use t(#{key.inspect}) shortcut because path is not available"
             end

つっつきボイス:@virtual_pathとな?やってることはメモ化だけど」「あ、#tのあたりか!: どこでどうなってるのかようわからんやつ」

参考: ActionView::Helpers::TranslationHelper#translate#tはこれのエイリアスです

「ついでに質問しちゃいますけど、ActionViewにあるRails備え付けのビューヘルパーと、自分で書いたビューヘルパーって、どこかで一箇所に集まっちゃうというかまとめてグローバルになっちゃうんですよね?」「そうですね、同じところというかview_contextっていうのがあって、レンダリングではそいつが大もとみたいな感じになってるんですよ」「あー」「そしてそいつにヘルパーどもがインクルードされまくってるんですよ」「なるほどねー!」「自分でビューヘルパーひとつも書いてなくても、既にヘルパーが大家族状態ってことか」

参考: view_context

「ActionViewヘルパーの他にもActionController::HelpersやらAbstractController::Helpersやらもあって、そういうのが大挙してview_contextに押し寄せてる感じなんですよ(爆)」「やべー」「やべー」「インスタンス変数とかもこいつにどんどんアサインされていって居座るし」「おとろしい…」

「Railsでコントローラに書いたインスタンス変数をビューで参照できるなんて、裏でこっそり何かやってないとできるはずがないですからね」「そうそう、その設計思想がどうもなー: ビューに渡したいものがあったら自分で書きたいわ」「ビューに渡したくないインスタンス変数だってあるのに、全部渡されちゃう」「でインスタンス変数を増やすとだんだんわけわからなくなってくると」

⚓追伸

morimorihogeさんから補足情報をいただきました。オプションで変えられるということです。

参考: views_assigns

⚓ActionDispatch::StaticのパスをUS-ASCIIからASCII-8BITに変更

さようなら.htmlのような日本語ファイル名も使えるようにということのようですが、極力したくないかも。

# actionpack/lib/action_dispatch/middleware/static.rb#L37
       if match = paths.detect { |p|
-        path = File.join(@root, p.dup.force_encoding(Encoding::UTF_8))
+        path = File.join(@root, p.b)
         begin
           File.file?(path) && File.readable?(path)
         rescue SystemCallError
           false
         end

       }
-        return ::Rack::Utils.escape_path(match)
+        return ::Rack::Utils.escape_path(match).b
       end
     end

つっつきボイス: 「static/とかのhtmlファイルなんかが対象ってことですかね」「fixtureにあるactionpack/test/fixtures/公共/foo/さようなら.htmlの「公共」ってsharedのことなんでしょうけど、日本人のセンスではなさそう」

⚓Active JobでQu gemのサポートを廃止

4年も更新のないQuがRails 5.1と互換性がなくなったためで、互換性が戻ることがあればまた追加するようです。

# activejob/test/integration/queuing_test.rb#L84
   test "should supply a provider_job_id when available for immediate jobs" do
-    skip unless adapter_is?(:async, :delayed_job, :sidekiq, :qu, :que, :queue_classic)
+    skip unless adapter_is?(:async, :delayed_job, :sidekiq, :que, :queue_classic)
     test_job = TestJob.perform_later @id
     assert test_job.provider_job_id, "Provider job id should be set by provider"
   end

参考: bkeepers/qu

⚓Ruby 2.6ではURI#unescapeにモンキーパッチを当てない

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

# activesupport/lib/active_support/core_ext/uri.rb#L3
 require "uri"
 str = "\xE6\x97\xA5"
-parser = URI::Parser.new

-needs_monkeypatch =
-  begin
-    str + str != parser.unescape(str + parser.escape(str).force_encoding(Encoding::UTF_8))
-  rescue Encoding::CompatibilityError
-    true
-  end
-
-if needs_monkeypatch
+if RUBY_VERSION < "2.6.0"
   require "active_support/core_ext/module/redefine_method"
   URI::Parser.class_eval do
     silence_redefinition_of_method :unescape

つっつきボイス: 「Rubyでunescapeをちゃんとできるようになったから、バージョンを見てパッチをやめるってことですね」

⚓Rails

⚓ダウンタイムなしでスキーマ変更するには(Ruby Weeklyより)


つっつきボイス: 「後半ぐらいから『こうだったらいいのに』みたいな雰囲気ですね」「マイグレーション内のafter_deployってRailsのメソッドか?と思ったら見当たらないから手作りというか、こんなのが欲しいってことなんでしょうね」「何をもってしてafter_deployを判断するんだろか?」「マイグレーションにそういうフックがあったとしても怖くて使えないw」

「コメント欄の方も気になっちゃって: こんな条件付きマイグレーション↓ができたらすごくうれしいんだけど方法が見当たらない…と」「それたぶんコントロールしきれないと思うヨ: おとなしく移行のバッチなりrakeタスクなりでやった方がええんではないかしら」「マイグレーション、ただでさえ複雑なのに」

# コメント欄より
class RemoveColumn < ActiveRecord::Migration[7.0]
  on_next_deploy_after RenameColumn
  # ↑こういうのとか↓こういうのとか
  when RenameColumn, deployed_for: 1.week

  def up
     remove_column :posts, :name
  end
end

⚓RailsとTDDとDHHと


つっつきボイス: 「ツイートでリンクされていた永和システムマネジメントさんのブログ記事がとても面白かったので: かの有名なt_wadaさんとの対談シリーズですね」


twop.agile.esm.co.jpより

「『テスト駆動開発自体が自己目的化してしまった』とか」「手段と目的が入れ違ってというか」「何がしたかったのか思い出せなくなったというか」「このあたりとか考えさせられます↓」

だけど、先鋭化したテスト駆動開発や、例えばクリーンアーキテクチャーのような依存を逆転させて清潔にしたレイヤリングとRuby on Railsの強みは明らかに対立関係にあったんですね。
同対談より

「クリーンアーキテクチャってたしかこれ↓じゃ?」「あーそうだった!: morimorihogeさんが勉強会で見せてくれてた」


https://8thlight.com/blog/uncle-bob/2012/08/13/the-clean-architecture.htmlより

「こうしてみるとRailsは元々プロトタイプを素早く作る感じで、そもそもクリーンアーキテクチャとは目指すものが違うというか、だいたいコントローラにアクションが全部集まってるし」「この間のSOLID記事にも反してるし: まあ記事書いた人は反してないって言ってますけどw」「『ここは俺の家だ!』みたいな」

「でDHHが『TDDは死んだ』と宣言してセンセーションを巻き起こしたと」「たしかその当時、ケント・ベックマーティン・ファウラーとDHHの対談がストリーミングで流れてて、『どうしてこうなった』みたいな話をしてましたね」「お、探します」

↓こちらです。Part IVまであってめちゃ長いのでPart Iのみです。

「ところで教条主義って言葉は私の年だと微妙に懐かしいんですけど、これって昔の学生運動のときによく使われた言葉だったんですよね: 『この教条主義者ガー』みたいな感じで」

⚓書籍『Refactoring』(Martin Fowler)第二版がリリース

同書はJava向けですが、Ruby向けのRefactoring for Rubyも定番です。惜しくも日本語版は割とすぐに絶版かつ中古で高値が付いています


つっつきボイス: 「ちょうどそのMartin Fowlerさんの本ですね」「日本語の『リファクタリング:Rubyエディション』、中古で8000円かよ!」「これ、社内に1冊あったはず」「(社内の図書管理システムを参照して)お、あったあった」「これは借りなきゃ損損」「お、図書記録にhachi8833さんが『翻訳がとてもよい』って書いてるゾ」「(忘れてた…)」

⚓Railsで双方向関連付けをテストする(Hacklinesより)

# 同記事より
associations.each do |assoc|

  # stuff from previous example
  if too_hard
    next

  # self join (model has an association to the same model)
  elsif assoc.klass == model
    assert assoc.options[:inverse_of].present?, "Only half of a self-join association is defined in app/models/#{model.to_s.underscore}.rb. The inverse of :#{assoc.name} is not defined."
    assert model.first.respond_to? assoc.options[:inverse_of], "The inverse of :#{assoc.name} is not functioning correctly. Check app/models/#{model.to_s.underscore}.rb."

  # Explicitly defined inverse association. Must have an inverse defined in both models.
  elsif assoc.options[:inverse_of].present?
    assert associated_object = assoc.klass.to_s.singularize.camelize.constantize.first, "#{assoc.name.to_s.camelize} not present in fixtures. Check test/fixtures/#{assoc.name.to_s.pluralize.underscore}.yml.\nAssociation name: #{assoc.name}\nAssociated object: #{assoc.klass}"
    model_method = assoc.options[:inverse_of].to_s
    assert associated_object.respond_to?(model_method) || associated_object.respond_to?(model_method.pluralize), "#{associated_object.class.to_s.camelize} is not properly associated with #{model.to_s} via :#{model_method}. Check for the correct association in app/models/#{model.to_s.underscore}.rb."

  # Using an alias to reference another model. Should probably have inverse relationships explicitly defined.
  elsif assoc.name != assoc.klass
    assert associated_object = assoc.klass.to_s.singularize.camelize.constantize.first, "#{assoc.name.to_s.camelize} not present in fixtures. Check test/fixtures/#{assoc.name.to_s.pluralize.underscore}.yml"
    model_method = model.to_s.underscore
    assert associated_object.respond_to?(model_method) || associated_object.respond_to?(model_method.pluralize), "#{associated_object.class.to_s.camelize} is not properly associated with #{model.to_s} using :#{model_method} as the inverse of :#{assoc.name}. Check for the correct associations and :inverse_of attributes in app/models/#{assoc.klass.to_s.underscore}.rb and app/models/#{model.to_s.underscore}.rb."

  # Normal bidirectional association
  else
    # do the stuff from earlier
  end
end

つっつきボイス: 「な、何だこれはw」「こんなにしてまでアソシエーションが抜けてないかどうかをテストする意味ってあるんだろか?」「コード書き終わったらいらないですよね、確かに」「テスト残す価値ないわ」「文章まだ読んでませんが、もしかしてたとえば外注先のコード品質が信用できなくて、怒りに任せてこういうテストを書いてから発注したとかですかね?」「テストファーストの匂いがする」

「ApplicationRecordにだったらこれっぽいのを書きたいかなと思ったりしますね: cancancanにあるcheck_authorizationなんかそうですけど、これをApplicationRecordに書いておくと、継承先でauthorizeを呼んでないときにエラーになる」「おー!」

「つかアソシエーションがあるかどうかをrespond_to?でテストするのが一番楽じゃないっすか?」「確かにー」「Railsチュートリアルにも確かそんな感じで書いてありましたよ: respond_to? childrenみたいに」「そうじゃなくてもモデル名使えばいいのに」

[翻訳]RailsチュートリアルがRails 5.0に完全対応しました!(第4版)?

⚓コールバックとインスタンス変数

# 『3年以上かけて培ったRails開発のコツ集大成(翻訳)』2.より
class UsersController
  before_action :load_resource

  def index
    # @usersで何かする
  end

  def show
    # @userで何かする
  end

  def create
    # @userで何かする
  end

  def update
    # @userで何かする
  end

  def destroy
    # @userで何かする
  end

  private
    def load_resource
      case params[:action].to_sym
      when :index
        @users = paginate(apply_filters(User.all, params))
      when :create
        @user = User.new(create_params)
      when :show, :update, :destroy
        @user = User.find(params[:id])
      end
    end
end

つっつきボイス: 「以前のTechRacho記事『3年以上かけて培ったRails開発のコツ集大成(翻訳)』でダントツで疑問が多く寄せられた『2. コントローラにload_resourceを書く』です」「うん、これキライ: indexしかないとかならまあわからんでもないけど、caseとかまで書いちゃうのはなぜ?みたいな」「厳密にスコープを分けたいとか常に何らかのスコープを通したいときとかなんですかね:気持ちはワカルけど」「全アクションに業務ロジックを書きたくないとか」「でもたとえばindexがいなくなったら台無し」「DRYに書こうとしすぎた感じですかね」

「怖いのは、新人くんとかにこれを盲目的にやられてしまうことかな」「あーたしかに」「何だかこう、コピペの匂いがするんですよね: これコピペしてやっとけって指導するみたいな」「これならコピペで動いちゃいますからね」
「このコードだとindexをpagenateしてるけど、後で特定のindexでpagenateを使わないように変えようとすると詰む」「CSV出力みたいに、indexをページネーションしないとかざらにありますしね」

3年以上かけて培ったRails開発のコツ集大成(翻訳)

⚓(社内雑談より)Railsの設計で崩れやすいのは例外処理とロギング

つっつきボイス: 「この間タバコ吸いながらそういう話題になったので」「経験上、例外処理もロギングもまったく念頭にないまま書かれた素人さんコードに、後からそれを足そうとして詰む、みたいなのがよくあるかな」「ロギングはともかく、例外の方はそもそも正常系がぜんぜんちゃんとしていなかったりすると例外の拾いようがなくなって作り直す方が早かったり」「エラーの扱いが全然一貫してなかったりとか」「true/falseで返すなよ、とか」

「ボクが例外処理が好きなのは、異常時にガード的にどんどん終わってくれるからコードがシンプルになるところ: if書かなくていいし」

Rubyスタイルガイドに『例外処理を制御フローに使わないこと』ってあったのを思い出しました」「そりゃやっちゃダメですよねw」「いわゆるgoto的に使うもんじゃない」

参考: Avdi Grimm氏の有名な「Exceptional Ruby」のスライドです。


Exceptional Rubyより

参考: Exceptional Ruby — 同氏の著作です。


exceptionalruby.comより

⚓その他記事など

⚓Ruby trunkより

⚓桁数が多いときにFloat#round(n)の結果がずれる(継続)

# 同issueより
3.0e-31           #=> 3.0e-31
3.0e-31.round(31) #=> 3.0000000000000003e-31

つっつきボイス:Rational#to_fで回避する、と」「Floatって忘れた頃につまずきますよね」

Rationalがデフォルトだったらいいのになんて思ってしまいました。

⚓コロンを変なところに置くと落ちる->(#14261と重複)

def a(x)
  case x
  when A
    :      # これ
  when B, C
  end
end

つっつきボイス: 「おー、これで落ちるのか」「『よくぞ見つけた』みたいなこと書いてる」「と思ったら#14261で修正されてた」「nobuさん、凄っ」

⚓(提案)ハッシュで特定のキーへの最短パスを取るメソッドが欲しい(継続)

# 同issueより
{
  :a => {
    :name => "abc"
  },
  :b => {
    :name => "xyz"
  },
}
deep_key(:name) => [:a]

つっつきボイス: 「これは欲しいときあるかも」「Rubyのハッシュキーの順序が保証されていなかったら大変そうですね」「『他の言語にあるからRubyでもやろうよ』というアピールは効きそう」「たしかPythonも最近順序が保証されるようになってた」

参考: Rubyリファレンスマニュアル Hash

⚓Ruby

⚓k0kubunさんのbenchmark/driver

ベンチマークの別記事も見つけました。


つっつきボイス: 「k0kubunさん今回は英語ブログだけ書いてるみたいですね: 両方書くのは大変だからかな」「そういう境地になりたいw」「ちょうど昨日derailed_benchmarksのREADME翻訳を出したところでした↓」

Rails: 多機能ベンチマークgem「derailed_benchmarks」README(翻訳)

⚓あまり知られてないっぽいRubyのフック系メソッドたち



つっつきボイス:includeじゃなくてincludedみたいに過去分詞というか受動態というか」「たとえばincludedは、includeしたときに発動するコールバックみたいな感じですね」「この名前、コールバックっぽく見えないなぁ: on_includeとかなら…」「でも何度も見かけるうちにわかるようになっちゃいましたねw」「method_removedとかmethod_undefinedとか他にもまだまだあるんですね」

「そういえばincludeされるに呼ばれるフックとかもあった気がする: んーと、append_featuresだ↓」「こりゃすげえ」「こんなことまでできちゃうの?って正直びっくりでしたね」「呼び出しコスト大丈夫なのかしらん?」

参考: append_features

「フレームワーク的なものを作るときに欲しいやつで、業務で使ったらヤバイ」「includedは、言ってみればRailsのconcernsみたいなものだし、ありかな」「おー、concernsをそうやって説明してもらったら何だか腑に落ちた(気がする)」

参考: ActiveSupport::Concern

⚓Browser: Rubyでブラウザ情報を検出するライブラリ(Ruby Weeklyより)

# 同リポジトリより
...
browser.device
browser.device.id
browser.device.name
browser.device.blackberry_playbook?
browser.device.console?
browser.device.ipad?
browser.device.iphone?
browser.device.ipod_touch?
browser.device.kindle?
browser.device.kindle_fire?
browser.device.mobile?
browser.device.nintendo?
browser.device.playstation?
browser.device.ps3?
browser.device.ps4?
browser.device.psp?
browser.device.silk?
browser.device.surface?
browser.device.tablet?
browser.device.tv?
browser.device.vita?
browser.device.wii?
browser.device.wiiu?
browser.device.xbox?
browser.device.xbox_360?
browser.device.xbox_one?
...

つっつきボイス: 「Wii UとかXboxまであるじゃないですか」「何だかスゴイ」
「遠い昔に、当時の同僚であらゆるブラウザのUAヘッダをコレクションしてた人がいて、新しい携帯を買うたびにヘッダ取らせろってせがまれたのを思い出した」「今だともうコンプリート不可能ですね」

⚓Module.class_execLiquidを動かす(RubyFlowより)

Object.instance_execModule.class_execは似ているけどちょっと違うみたいです。翻訳リクエストしてみます。

⚓GnuPGをRubyで実装した話(RubyFlowより)

⚓multi.rbって使われてる?

# 元記事より
require 'rubygems'
require 'multi'
multi(:fact, 0) { 1 }
multi(:fact, Integer) { |n| n * fact(n-1) }

for i in (0..5)
  puts "fact(#{i}) ==> #{fact(i)}"
end

Matzのプログラミングのおきてをネットでけちけち読んでいて「マルチメソッド」という見慣れない言葉を見つけたので探してみました。


つっつきボイス: 「マルチメソッドって有名なんですか?」「知らないー」「(コードを見て)ほほう、なるほどー❤」「おーなるほどー❤」「すみません、面白みのところ教えてください🙇

「普通だったらfactメソッド作ってifで分岐するところだけど、Haskell的にifなしで書ける: 0が来たときは1つめのmulti、それ以外が来たときは2つ目のmultiが動くみたいな」「あー!言われてみればすごく数式っぽい: 数学的帰納法みたいな形」「関数型っぽいすね」
「まあでもこの書き方だと、パラメータの違う同じメソッドを複数定義するから、定義があちこちで増えたときにどれに解決するのかとかいろいろ面倒そう」「業務では使わないだろうなー」「処理として2つのmultiがあるところが自分的には残念: その間に何を実行されるかわからないでしょ?」「おー確かに」「処理とは別に定義としてこういうのを書けるんだったらきっと好き」

そういえば日本語記事にも「おもしろいんだがあまり使い勝手は良くなかった」って書いてありました。

プログラミングと直接関係ありませんが、引用したWikipediaにこんなことが書いてあります。

なお、数学的「帰納」法という名前がつけられているが、数学的帰納法を用いた証明は帰納ではなく、純粋に自然数の構造に依存した演繹論理の一種である。2 により次々と命題の正しさが”伝播”されていき、任意の自然数に対して命題が証明されていく様子が帰納のように見えるためこのような名前がつけられたにすぎない。
同Wikipediaより強調

100年遅いですが、個人的には数学的帰納法(mathematical induction)は「数学的誘導法」と訳して欲しかった…

⚓その他記事など

⚓SQL

⚓データベースの「トップN」問題を解決する(Postgres Weeklyより)

CitusData社のpostgresql-topnツールを紹介しています。


つっつきボイス: 「トップN問題って、N+1クエリみたいなアンチパターンとは違いますよね」「たぶんだけど、『トップ5の項目をすべてのカテゴリについて取る』みたいなやつなのかな: それなら確かに面倒かも」「Ruby側で処理すると効率悪いっすね」「DB側でできるんだとしたら便利かもー」

⚓pg_repack: PostgreSQLデータベースのテーブル再編成を最小のロックで

公開しそびれた翻訳記事で紹介されていたので。CLUSTERやVACUUM FULLと違って処理中のテーブルの排他ロックを維持せずにできるそうです。

⚓Awesome PostgreSQL: 精選ソフトウェア/ライブラリ/ツール/リソースリスト(Postgres Weeklyより)

Awesome MySQLを見て作ったそうです。どちらも参考になりそうです。

⚓JavaScript

⚓Vue.jsをRailsのフロントエンドとして使う(Ruby Weeklyより)

// 同記事より
import TurbolinksAdapter from 'vue-turbolinks';
import Vue from 'vue/dist/vue.esm'
import App from '../app.vue'

Vue.use(TurbolinksAdapter)

document.addEventListener('turbolinks:load', () => {
  const app = new Vue({
    el: '#hello',
    data: {
      message: "Can you say hello?"
    },
    components: { App }
  })
})

⚓Conditioner.js: プログレッシブエンハンスメントをストレートに実現するJSライブラリ(JavaScript Weeklyより)

プログレッシブエンハンスメントは、核となるコンテンツを最重要視するウェブデザイン戦略である。この戦略では、エンドユーザーのブラウザーやインターネット接続に合わせて、プレゼンテーション面や機能面で微妙に異なる内容や技術的に困難な内容をコンテンツに漸次追加していく。この戦略の利点として挙げられるのは、すべてのユーザーが任意のブラウザーまたはインターネット接続を用いてウェブページの基本的なコンテンツと機能性にアクセスできることと、より高度なブラウザーソフトウェアまたはより広帯域の接続を有するユーザーには同じページの拡張バージョンを提供できることである。
Wikipedia-jaより

参考: Wikipedia-ja プログレッシブエンハンスメント

⚓(動画)Vue.jsコンサルタントが本当は教えたくない7つの秘密パターン(JavaScript Weeklyより)

⚓その他記事など

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

⚓VisualInspector: 開発とデザインの齟齬をなくすCanvasFlip社のサービス(Hacklinesより)


sitepoint.comより

チーム開発でレイアウトをピクセル単位でびしっと決められるそうです。この種のサービスが最近増えてきているように思えるので。

⚓display: contentsのしくみ(Frontend Weeklyより)


同記事より

CSSのdisplay: contentsの解説記事です。

参考: MDN display

⚓CSSの「Typed Object Model」について(Frontend Weeklyより)

Typed OMに関するGoogleの技術ブログです。文字列の代わりにオブジェクトベースのAPIを使ってJavaScriptからCSSを制御できるようです。

// 同記事より
el.attributeStyleMap.set('padding', CSS.px(42));
const padding = el.attributeStyleMap.get('padding');
console.log(padding.value, padding.unit); // 42, 'px'

記事の真ん中あたりにデモがあります(マウスオーバーすると四角形が回転する)が、私のChrome 65.0.3325.181では動かず、Chrome Canary 67.0.3383.0だと動きました。なお、今BPS社内ではbabaさんのおすすめでCanary版もインストールするのが流行中です。


developers.google.comより

最初わかってませんでしたが、CSS Object Model(CSSOM)を拡張したのがTyped OMという位置づけだと教えてもらいました。

CSSOM値の文字列を意味のある型付きJavaScript表現に変換(および逆変換)するとパフォーマンス上のオーバーヘッドが大きい。本仕様では、CSSの値を型付きJavaScriptオブジェクトとして公開することで効率よく操作できるようにする。
www.w3.org/TR/css-typed-om-1のAbstractより大意

参考: W3C CSS Typed OM Level 1

デモでついこのジャケットを思い出してしまいました↓。

⚓その他

⚓OracleとGoogleのJavaライセンス裁判

日本語記事です。とてもよくまとまってて助かります。ちょっと前に「GoogleがC#に乗り換えるかも?」みたいな記事を見かけたのですがうまく見つけられませんでした。

⚓ワイのLinux環境で使ってるツールを晒す(2018年度版)

知らないツールもいろいろあって楽しめます。「後はMacのAlfredみたいなランチャーやFenêtreみたいなピクチャ・イン・ピクチャ的ツールがLinuxにもあれば…」だそうです。


www.alfredapp.comより

⚓ReactiveX — Observableパターンのライブラリ

社内アプリチームがRxSwiftなどで盛り上がっているので。


つっつきボイス: 「ReactiveXって、何だかActiveXみたいな名前」「Rxなんちゃらのすべての元祖みたいなやつですね」「Observablesパターンが多用されるらしいんですけど」「Operatorsのページ見るとわかるけど、膨大なオペレーターがあるんですよね↓」「ほんとだー」


reactivex.ioより

参考: Rubyリファレンスマニュアル Observable

「ところでRubyでObservableパターンってよく使います?」「そういえば、確かそのものズバリのObservableってモジュール↓がありますね: #changedでフラグを立てて、notify_observersを呼ぶと、フラグの立っているクラスでupdateメソッドを呼ぶ形で通知する、みたいな」「おー」「かっけー」

# docs.ruby-lang.org: Observableより
require "observer"

class Ticker          ### 定期的に株価をフェッチ
  include Observable

  def initialize(symbol)
    @symbol = symbol
  end

  def run
    last_price = nil
    loop do
      price = Price.fetch(@symbol)
      print "Current price: #{price}\n"
      if price != last_price
        changed       # オブザーバーに通知
        last_price = price
        notify_observers(Time.now, price)
      end
      sleep 1
    end
  end
end

factory_botとかのコールバックでこのライブラリが使われてたような覚えがあります: after(:create)あたりとか」

# https://github.com/thoughtbot/factory_bot/blob/master/GETTING_STARTED.md より
factory :user do
  after(:create) { this_runs_first }
  after(:create) { then_this }
end

[保存版]人間が読んで理解できるデザインパターン解説#3: 振舞い系(翻訳)

⚓C言語のfor文が変わる?

// リンク先より
        static inline int f (void) {
            for (int i = 0; ; ) {
                long i = 1;   // CではできるがC++では無効
                // ...
                return i;     // (おそらく期待に反して) Cでは1が返る
            }
        }

つっつきボイス: 「次のC?」「どういうこったろ?」「うお、for文の中でlong i = 1なんて書いてる: Cって今までこんな書き方できてたの!」「あれ、そもそもCでfor (int i = 0; ; )みたいな書き方許されてたんだっけか?int i = 0の部分をforより前に書かないとダメだった気がするけど: C言語ワカラン」「時間ないので次へー」

「関係ないんですけど、どの回だったかな、例のTuring Complete FM聞いてたら、『Cの関数宣言の何がイヤって、最初に型書くから途中まで読み進めないとそれが関数定義なのかどうかがわからないところ』って話があったんですよ」「ワカルー」「最近多くの言語が型を後ろに書くようになったのって、きっとそれなんでしょうね」

⚓その他記事など

⚓番外

⚓自分の声で


つっつきボイス: 「自分の声ってたいてい聞きたくないやつですけど」「これちゃんとした日本のメーカーじゃないですか」「自分の声をそっくりに出せるのって、アリバイ工作とかに使えたりして?」「他にもアレやらコレやら(略」

⚓宇宙を閉じ込めたような球体

⚓地味に思われがちな固体物理

⚓請負の翻訳でやらせてもらえないやつ

⚓どこかで見たような…

たぶんこれかな。


今週は以上です。

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

週刊Railsウォッチ(20180323)Rails 5.2.0 RC2リリース、「サーバーレスなRubyが欲しい」、capybara風JSテストフレームワークCypressほか

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

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

Ruby 公式ニュース

Rails公式ニュース

Ruby Weekly

Awesome Ruby

RubyFlow

160928_1638_XvIP4h

Hacklines

Hacklines

Postgres Weekly

postgres_weekly_banner

Frontend Weekly

frontendweekly_banner_captured

Frontend Focus

frontendfocus_banner_captured

JavaScript Weekly

javascriptweekly_logo_captured

Github Trending

160928_1701_Q9dJIU

週刊Railsウォッチ(20180406)ruby-sass gemが非推奨に、Roda gem、paiza.ioは便利、Linuxは/procで遊ぼうほか

$
0
0

こんにちは、hachi8833です。記事数抑えたはずが遅くなってしまいました。

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

⚓Rails: 今週の改修

全体にrafaelfrancaさんのコミットが目立ちます。まずはまだ動いている5-2-stableから。

⚓Rack::BodyProxyでbodyが改変されていたのを修正

@app.callが返すオブジェクトが(定数などで)保存済みだと、Rack::BodyProxyでbodyをラップする継続サイクルでbodyが改変され、最終的にSystemStackErrorになる。以下は再現コード。
同PRより

# 同PRより
class HealthcheckApp
  SUCCESS_RESPONSE = [200, { 'Content-Type' => 'text/plain' }, ['success']].freeze

  def initialize(app)
    @app = app
  end

  def call(env)
    return SUCCESS_RESPONSE if env['PATH_INFO'] == '/heartbeat'
    @app.call(env)
  end
end

app = HealthcheckApp.new(-> (_x) { [200, {}, nil] })
logger = Rails::Rack::Logger.new(app)

logger.call('REQUEST_METHOD' => 'GET')

つっつきボイス: 「ほあっSystemStackErrorって割りとキツイやつ?」「修正前のはresp[2] =で配列を直接書き換えてますね↓」「ははー、なるほど!修正前はresp = @app.call(env)と配列が参照渡しになってた」「修正後のは変数展開で渡してるから書き換わらないと」
「直接書き換えてても普通に使われてる分にはほとんどの場合問題は起きなかったんでしょうけど」「使いまわしているうちにcontent lengthとかMD5あたりで不整合が生じそう」

# railties/lib/rails/rack/logger.rb
-          resp = @app.call(env)
-          resp[2] = ::Rack::BodyProxy.new(resp[2]) { finish(request) }
-          resp
+          status, headers, body = @app.call(env)
+          body = ::Rack::BodyProxy.new(body) { finish(request) }
+          [status, headers, body]

「しかしSystemStackErrorって心にインパクトあるというか、見ると絶望感に襲われそうw」「これだからconstできない言語は」「(爆)」

⚓ActiveRecord::QueryCacheミドルウェア内を最適化

# activerecord/lib/active_record/query_cache.rb#L28
     def self.run
-      ActiveRecord::Base.connection_handler.connection_pool_list.map do |pool|
-        caching_was_enabled = pool.query_cache_enabled
-
-        pool.enable_query_cache!
-
-        [pool, caching_was_enabled]
-      end
+      ActiveRecord::Base.connection_handler.connection_pool_list.
+        reject { |p| p.query_cache_enabled }.each { |p| p.enable_query_cache! }
     end

-    def self.complete(caching_pools)
-      caching_pools.each do |pool, caching_was_enabled|
-        pool.disable_query_cache! unless caching_was_enabled
-      end
+    def self.complete(pools)
+      pools.each { |pool| pool.disable_query_cache! }

       ActiveRecord::Base.connection_handler.connection_pool_list.each do |pool|
         pool.release_connection if pool.active_connection? && !pool.connection.transaction_open?

つっつきボイス:mapで回していたのをrejecteachに変えたと」「呼び出し多いだろうからmapで全回しじゃなくてrejectでフィルタしてからeachする方が効率いいぜ、ってことか」「ベンチマーク見たいなー: ないのかな?」

⚓params#digの修正

params = ActionController::Parameters.new(a: { b: { c: 1 } })
params.dig(:a, :b)[:c] = 2
params 
# 修正前=> <ActionController::Parameters {"a"=>{"b"=>{"c"=>1}}} permitted: false>
# 修正後=> <ActionController::Parameters {"a"=><ActionController::Parameters {"b"=><ActionController::Parameters {"c"=>2} permitted: false>} permitted: false>} permitted: false>
# actionpack/lib/action_controller/metal/strong_parameters.rb#L592
     def dig(*keys)
-      convert_value_to_parameters(@parameters.dig(*keys))
+      convert_hashes_to_parameters(keys.first, @parameters[keys.first])
+      @parameters.dig(*keys)
     end

つっつきボイス: 「確かにdigは修正前みたいな挙動を期待しないですよね普通: 上と似た感じのミューテーション問題」「参照渡しから値渡しへの変更」

「それにしてもこういう書き方↓されるとイヤだよねって思うw: 推奨したくない」「ものすごく追いにくいコード」「どう呼ばれても安全にしておくのがいい書き方デス!」「ほんとその通り!」

params.dig(:a, :b)[:c] = 2

⚓メソッド呼び出しのダブルsplat**を普通の引数に変更

続いてRails 6向けmasterから。

# actionview/lib/action_view/helpers/form_helper.rb#L1973
-        fields_for(scope || model, model, **options, &block)
+        fields_for(scope || model, model, options, &block)

つっつきボイス: 「Ruby 2.6ではダブルsplat **の使用が警告されるみたいですね」「上だけ見ると、メソッド定義側ではなくてメソッド呼び出し側のダブルsplatですね」「Rubyのその変更ってどこで知らされたんだろう?(記号探しにくい…)」「ruby 2.6.0dev (2018-04-04 trunk 63085)で出る警告だということはわかったけど」

「うーん**とか普段使わないからなー、pryでちょい試しているけどうまくダブルsplatのbad caseを出せない…: おー、呼び出し側では**があってもなくてもハッシュをキーワード引数として渡せる↓ことはわかった」

h = {a:1, b:2, c:3}
def hoge(arg=99, a:, b:, c:)
   puts "arg:#{arg}, a:#{a}, b:#{b}, c:#{c}"
end

hoge h   #=> z:99, a:1, b:2, c:3
hoge **h #=> z:99, a:1, b:2, c:3

「つ#12106: 2年前にオープンしたまま閉じてませんが、何かそれっぽいissue見つけたので誰か頑張って解読してください」「works fineってどの辺がfineなんだか」

Rubyのパラメータと引数の対応付けを理解する(後編)

⚓不要なActionController::を削除

# actionpack/lib/action_controller/metal/exceptions.rb#L25
-  class ActionController::UrlGenerationError < ActionControllerError #:nodoc:
+  class UrlGenerationError < ActionControllerError #:nodoc:
   end

修正そのものはシンプルですが、 rafaelfrancaさんのEiNSTeiN-:patch-1というブランチ名が何だか気になりました。


つっつきボイス: 「superfluous: 余分な、無駄な」「すごーく堅苦しい言葉ですね」

確かこの言葉を最初に見たのはスピノザの「エチカ」の英語版でした(原文はラテン語)。

⚓finalize_compiled_template_methodsをデフォルトでオンに

# actionview/lib/action_view/template.rb#L9
   class Template
     extend ActiveSupport::Autoload

-    mattr_accessor :finalize_compiled_template_methods
-    self.finalize_compiled_template_methods = true
+    mattr_accessor :finalize_compiled_template_methods, default: true

action_view.finalize_compiled_template_methodsはconfigなんですね。


つっつきボイス: 「んなオプション(゚⊿゚)シラネ」「この設定ってどこだろ?」「つ9facd9a

「ところで、このPRはどうやら単なる書式修正ですね」「お」「その前のeede8d8 Add action_view.finalize_compiled_template_methods config optionでの機能追加したときの書式が古かったからちょっと直したということみたい」「しまった、そっちが本編でしたか」

⚓Rails

⚓RubyのSassが非推奨に


sass/dart-sassより


つっつきボイス: 「今日Slackに流してもらった情報ですね」「しばらくちょい面倒なことになりそうな気がする: sass-railssassc-railsの両方に依存してたらどうなっちゃうんだろうとかw」「LibSassはC/C++で速そうではある」「しかしRuby版がなくなるのはちょっと悲しいなー: マイナーなアーキテクチャ上でちょっとした処理するときとか便利だったんで」

⚓Webpacker 4 pre版リリース(Hacklinesより)


つっつきボイス:Webpack 4はこないだ出てる(ウォッチで既報↓)から、Webpacker gemも追随してるということですね」「割と紛らわしい…WebpackとWebPacker」「もうpre2になってるし↓」「まあまだしばらくかかるかな」


github.com/rails/webpackerより

週刊Railsウォッチ(20180302)Ruby 2.6.0-preview1とWebpack 4.0リリース、爆速検索APIサービスAlgolia、Clowneでモデルをクローンほか


webpack.js.orgより

⚓Rails 5.2のcredentialをKubernetesで使う(Ruby Weeklyより)

Kubernetesは最近「クバ」とか「クーバァ」みたいにじわじわ略されつつあるような雰囲気?


つっつきボイス: 「確かにそろそろ呼び方統一してもいいんじゃね?」「記事の方は、Railsのcredentialsはそもそもマイクロサービス的なコンテナを意識したソリューションだから当然できますね」「確かに: 記事短いし、『やってみたらできた!うれしい!』という感じ」

⚓マイグレーション支援gem 4種

handcuffs(手錠)ってすごい名前。


つっつきボイス: 「strong_migrationsはTechRachoにも何度か登場してますね: 有名」「何というか微妙によくやってくれるヤツ」「この手のマイグレーションに機能を追加するのって怖くてしょうがないんですけど: 1個のマイグレーションファイルをずうっと書き換えるのこっそりやってますが何か」「何とw」「あ、でもそれってクックパッドさんでおなじみのridgepoleがまさにそれですよね: ridgepoleは結構いい」「ridgepoleを使うときは、マイグレーションの履歴を知りたければGitのログ見れということになる」「言われてみれば履歴を二重管理することもないですね」「本格的に使ったわけではないけれど、ridgepoleがつらくなることがあるとすれば、ridgepoleが対応していないDBMSの独自機能をいじり始めたときかな」

参考: クックパッドにおける最近のActiveRecord運用事情 - クックパッド開発者ブログ

「zero_downtime_migrationsはおそらくPostgreSQLの機能を使ってやるんじゃないかな: PostgreSQLに確かそういうスキーマ変更時のロック回避関連の機能があった」

「outriggerはマイグレーションにタグを打てるみたいですね↓」「いやぁ、マイグレーションでタグが必要になる運用って何か間違ってる気がする…」

# instructure/outriggerより
class PreDeployMigration < ActiveRecord::Migration
  tag :predeploy
end
class PostDeployMigration < ActiveRecord::Migration
  tag :super_fun
end

Switchman::Rake.shardify_task('db:migrate:tagged')

「handcaffsもマイグレーションにphaseを設定できますが、じゃこれも?」「同じくその運用自体がどうかと思うし、マイグレーションを複雑にするのは何かが間違ってる気がする…そう思わない?」「フレームワークが整備される前は機能としてのマイグレーションはなかったし、本番リリースすれば捨てていいぐらいのものだったはずだし」「そうそう、マイグレーションがソースコードとして永続化するのは方向性として何か違う気がする」「欲しいのは本来スキーマ」「昔は皆てんでばらばらにマイグレーションのバッチを書き捨てて追えなくなっちゃったりしただろうから、Railのマイグレーションファイルはそうした書式と置き場所を統一するためのものだったのかもですね」

「そういえば以前Excelにテーブルを書くとSQLを生成するマクロを作りましたよ: 今もこっそり使われてますが」「そうそうSIerがよくやってるヤツ」「新規はともかく変更し始めるとすごく面倒くさかったけどw」「そもそもRailsはスキーマ変更頻繁すぎ」「まあそれはRailsの思想ということで」

⚓Roda: プラグイン方式の柔軟で強力なルーティングエンジン


roda.jeremyevans.neより

y-yagiさんが最近好きなフレームワークだそうです↓。近々READMEの翻訳をTechRachoで公開しようと思います。


techplay.jpより

なお、以前翻訳したRodauthもRodaを全面的に使っています。Developers Meetupのアフターパーティで「RodauthのREADMEは名ドキュメント」とy-yagiさんとうなずきあいました。

Ruby: 認証gem ‘Rodauth’ README(翻訳)


つっつきボイス: 「Sinatraのルーティングがベースになってるらしいです」「あー、ネストで書けるのかなるほど!」「ソースを見ると、本体はわずかで、ほとんどの機能をプラグインとして装備してました」

# http://roda.jeremyevans.net/より
# cat config.ru
require "roda"

class App < Roda
  route do |r|
    # GET / request
    r.root do
      r.redirect "/hello"
    end

    # /hello branch
    r.on "hello" do
      # Set variable for all routes in /hello branch
      @greeting = 'Hello'

      # GET /hello/world request
      r.get "world" do
        "#{@greeting} world!"
      end

      # /hello request
      r.is do
        # GET /hello request
        r.get do
          "#{@greeting}!"
        end

        # POST /hello request
        r.post do
          puts "Someone said #{@greeting}!"
          r.redirect
        end
      end
    end
  end
end

run App.freeze.app

「まあわかるなー: 何しろRailsのルーティングは最近になるほどよくわからなくなってきてるから(爆)」「この間もそんな話になりましたね」「Railsのルーティングの完全なドキュメントが欲しいし、ルーティングまじで難しい: ルーティングをネストしても必ずしもURLがネストされるとは限らないからかなり不可思議な感じになるとか」

「結局Railsガイドのルーティングが頼りってことですかね?日本語まだ追いついてないですが(やらなきゃ…)」「つguides/source/routing.md、とよく見たらRailsガイドのソースか」「ルーティングにconcernあるんですけど?」「そう、あるんですよ」「defaults: { format: 'jpg' }あたりからだんだん不思議感あふれてくる」「constraints、これ割りと便利だけど気をつけないと死ぬ: ルーティングが重複しているときに記述の順序で評価順序が変わってハマったことあった」「to: MyRackAppでRackアプリを指定できるんでlambda書けるし」

「やっぱり最新のガイドかー」「だいたいは見覚えあるけど、知りたいのはこれが組み合わさるとどうなるのかってところなんですけどねw」

「話逸れますが、Railsのルーティング機能って何か名前が付いてましたよね: Developers Meetup2日目オーラスの対談で耳にしたときに何のことだかわからなくて」「journey」「それだっ」

参考: action_dispatch/journey

「線路」と「旅」としゃれたんでしょうね。

Hanami 2.0でRodaを採用しようという提案も出されてるんですよ: breaking changeを承知で」「Roda、きれいに書けるしHanamiに入るのはいいんじゃないかな」

Railsのルーティングを極める(前編)

⚓GoCD: デプロイをビジュアル管理するサービス(Ruby Weeklyより)


gocd.orgより

サイトの図のデザインがかわゆくて個人的に好きです。図がどうもカッコよく作れない人は、描線を思い切って太くするだけで相当印象変わるとこっそり思っています。


gocd.orgより


つっつきボイス: 「最近こういう感じのをよく見かけますね: ↓これGitLabにも同じようなのが入ってる」「ほんとだ」「AWSにもこういうのあるし、多くのCIに取り入れられてますね」

「AWSはCIとCD両方できる」「前にも話したけど、このCIとCDとCDって略称どうにかならない?w」「ですねー: 短くてかぶってて扱いづらい」

  • CI: Continuous Integration
  • CD: Continuous Deployment
  • CD: Continuous Delivery

⚓昔のRailsのdynamic finderって何がまずかったの?

ツイートで見かけて気になったので、ラジオのお葉書感覚で拾ってみました。


つっつきボイス: 「dynamic finderは昔のRailsにあったfind_なんちゃらみたいなやつということで」「やっぱりメソッドが大量に生成されたのと、method_missingで取るのが嫌がられたとかでしょうね: method_missingだとデバッグも拡張もしづらいし」「かといって前もって生成するという方向にもならなかったし」

参考: Rubyリファレンスマニュアル method_missing
参考: activerecord-deprecated_finders — gemに切り出されたんですね

Rails3のfind系メソッドと注意のまとめ

まったく個人的にですが、method_missingというと排水口に仕掛ける網みたいなのを連想してしまいます。

⚓ActiveRecordを非同期でマイグレーションする(Hacklinesより)

# 同記事より
class MigrationProcessingJob < ApplicationJob
  def perform(params)
    async_migration = AsyncMigration.find(params.fetch(:async_migration_id))

    all_migrations = migration_context.migrations
    migration = all_migrations.find { |m| m.version == async_migration.version }

    # actual work!
    ActiveRecord::Migrator.new(:up, [migration]).migrate

    async_migration.update!(state: "finished")
  end

  def migration_context
    ActiveRecord::Base.connection.migration_context
  end
end

つっつきボイス: 「ははー、ジョブでマイグレーションやる感じか」「Rails Developers Meetupでからあげエンジニアことささたつさんのプレゼンで、彼がやっているClassidb:migrateがすごく時間かかるという話をしてたんですが、そういう状況ではこういう形のasyncマイグレーションというのは検討の余地あるかも」「Classiさんはこの分野でけっこうシェアを伸ばしているそうでスゴイ」

その後、学校とインターネット接続の話題でしばし盛り上がりました。


classi.jpより

その他記事など

⚓Ruby trunkより

⚓Stack consistency error

/bundle/gems/sprockets-3.7.1/lib/sprockets/mime.rb:122: [BUG] Stack consistency error (sp: 960, bp: 959)
ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-linux]
(snip)

つっつきボイス: 「あまり見かけないエラーな気がして」「Stack consistency error見たことない…」

⚓ISeq#to_binary -> load_from_binary -> evalすると落ちる

$ ./miniruby -ve 'str = "class P; def p; end; def q; end; E = \"\"; N = \"\#{E}\"; attr_reader :i; end"; iseq = RubyVM::InstructionSequence.compile(str); bin = iseq.to_binary; RubyVM::InstructionSequence.load_from_binary(bin).eval'
ruby 2.6.0dev (2018-04-02 trunk 63063) [x86_64-linux]
Segmentation fault (core dumped)

⚓prependされたモジュールからシングルトンメソッドを取れない

module Empty; end

class MyClass
  singleton_class.prepend(Empty)

  def self.foo; end
end

MyClass.singleton_methods(false) # => [:foo]
MyClass.singleton_method(:foo) # => NameError (undefined singleton method `foo' for `MyClass')

これと関係ありそうな記事がちょうどRubyFlowに流れてきました。


つっつきボイス:singleton_methods?普段使わないけど」「私もtakanekoさんに教えてもらって知りました: シングルトンメソッドはクラスメソッドだということです」

参考: Rubyリファレンスマニュアル Object#singleton_methods

⚓Ruby

⚓Rubyで時間を「正確に」測る方法(Ruby Weeklyより)

# 同記事より
starting = Process.clock_gettime(Process::CLOCK_MONOTONIC)
# time consuming operation
ending = Process.clock_gettime(Process::CLOCK_MONOTONIC)
elapsed = ending - starting
elapsed # => 9.183449000120163 seconds

つっつきボイス:RTCで時間取れとかそういう話?」「『時間は一方向に進むとは限らない』みたいな見出しがちらっと見えた」「まあ実際そう: monotonicという概念とか」「monotonicって、数学の『単調に増加する』の単調ですね」
「Androidアプリとか、バックグラウンドに回るとクロックが嘘ついたりするんで」「ええぇ!」「時間を頻繁に測りすぎたらいけないとか何とかあってですね」

「この辺の時間がらみの知識はパフォーマンスとかベンチマークをやるときには必須で、ちゃんとしたコンピュータサイエンスの本だと最初の方で解説することが多いですね」「測定そのものにも時間がかかると」

参考: 時間情報の取得 clock_gettime() - 時間の扱い - 碧色工房 — 時間取得について詳しく書かれています

⚓searchkick: ユーザーの検索パターンを学ぶ検索gem(Awesome Rubyより)

Elasticsearchを使っているそうです。chewy gemと比較されていました

# 同リポジトリより

class Product < ApplicationRecord
  searchkick language: "german"
end

Product.search_index.tokens("Dish Washer Soap", analyzer: "searchkick_index")
# ["dish", "dishwash", "washer", "washersoap", "soap"]

Product.search_index.tokens("dishwasher soap", analyzer: "searchkick_search")
# ["dishwashersoap"] - no match

Product.search_index.tokens("dishwasher soap", analyzer: "searchkick_search2")
# ["dishwash", "soap"] - match!!

つっつきボイス: 「表記ゆれやスペルミスとか部分一致に対応する感じですね: クックパッドさんとかが自力で頑張ってる分野」「見た感じ英語だけなのかな?」

Elasticの日本語プラグイン: kuromojiが使えるようです。

「タマネギ、たまねぎ、玉ねぎ、玉葱のどれで検索してもヒットさせるとか、単にネギって書いたときに長ネギとタマネギと万能ネギのどれにヒットさせるか、みたいな処理とか」「そういうのをちょっとだけお気楽にできるようにするんでしょうね」

⚓Kernel#at_exit、初級編、中級編、魔境編(Awesome Rubyより)

# 同記事より
puts "start"

at_exit do
  puts "start of first at_exit"
  at_exit { puts "nested inside first at_exit" }
  at_exit { puts "another one nested inside first at_exit" }
  puts "end of first at_exit"
end

at_exit do
  puts "start of second at_exit"
  at_exit { puts "nested inside second at_exit" }
  at_exit { puts "another one nested inside second at_exit" }
  puts "end of second at_exit"
end

puts "end"

つっつきボイス:at_exit?フック系か」「これって2回書いたら後のやつが上書きしちゃいます?」「書いたのと逆順に実行しますね」「お、ちゃんとスタックに積むのか: それはスバラシイ」「Ruby技術者認定試験には何年か前に出ましたヨ」「やべーw」「あの試験にはこういう知る人ぞ知るみたいなものが出ますねー」

at_exit、何かで見たことありますね: スレッドが分かれた後の後始末みたいなところとか」「プログラム自体が強制終了しない限りは終了時に実行してもらえる感じなんでしょうね」「/tmpの下に作ったゴミを終了時に片付けるとか」

参考: RubyリファレンスマニュアルKernel.#at_exit

⚓継続的開発のつらさ(それはそうとStruct.newを継承すんな)(Awesome Rubyより)

class Country < Struct.new(:request, :ip, :country_code, :country_code2, :country_code3, :country_name, :continent_code)

つっつきボイス:Rubyスタイルガイドで『Struct#newで初期化したインスタンスを継承しないこと』ってあったのを思い出したので」「それはやっちゃダメなやつ: さすがにこういうのは見たことないけど」「…何かで見た気がする」「…やったことある: 単にattr_accessorとコンストラクタを書く手間を省きたかった、以上」「ま気持ちはわかりますw」「この書き方するとRubyMineとかで捕捉できなくなっちゃうんですよね」

⚓RejectKaigi 2018も開催

現時点では参加フォーム準備中だそうです。登壇受け付けは行われています。


つっつきボイス: 「RejectKaigiっていう名前がイイですね: Rで始まってるとことか」「韻を踏んでるw」「本編のRubyKaigiはあまりRails寄りの話が出なかったりするんで、むしろこっちの方がRails寄りとか業務運用寄りの話聞けるチャンスあるかも」「凄いのは、これRubyKaigiの公式イベントなんですよね」「ホントだ」

⚓Ruby 3×3のゴールを目指して

RubyKaig 2017のラス前↓を務めた、大御所Vladimir Makarovさんの記事です。


つっつきボイス: 「出たマカロフさん」「やっぱりJITは大きな話題だよね: まJITを入れるとベンチマークをいくらでも偽装できるみたいなところがないわけではないけどw: 長いんでこれは後で読もう」

⚓cron的なRubyプログラム対決


つっつきボイス: 「ところでcronの何がいいって、他が死んでもめったに死なないことだよね」「godなんてすぐ死にますよ」「godなのに(´・ω・`)」「で結局安定してる監視ソフトとなると昔ながらのmonitになる: config書きにくいけどなっ」「monitは大量にエントリ書くと全部グローバルになって、あれがつらい: 前にも話したsupervisorは一応{}で括れるから本当はsupervisorの方がいいんだけどなー」

⚓🌟paiza.ioは便利🌟


paiza.ioより


つっつきボイス: 「Paizaこういうのを始めたのか」「そうそう、paiza.ioはいいですよー、面接相手に使ってもらうときなんかにとっても便利!: ログインいらないし対応言語豊富だしIDEっぽいことたいていできるし」「Chromebookとかで使ってもらえば後始末もいらなくてさらに安心」

久々に🌟を進呈いたします。おめでとうございます。

⚓その他記事など

⚓SQL

⚓PostgreSQLでLLVM JITサポートに向けて取り組み開始(Postgres Weeklyより)


同記事より


つっつきボイス: 「ぽすぐれまでJIT!」「SQLクエリをJITコンパイルするのか: むしろとっくにやってたのかと思ってた」「前にウォッチでも話したウィンドウ関数とかJITが効きそうなのに」

⚓PostgreSQL 10向けにみっちりコメントのついたconfigファイル(Postgres Weeklyより)

# 同設定ファイルより

...
# max_connections
# ------------------------
# An integer setting a limit on the number of new connection processes which
# PostgreSQL will create.  Should be set to the maximum number of connections
# which you expect to need at peak load.  Note that each connection uses
# shared_buffer memory, as well as additional non-shared memory, so be careful
# not to run the system out of memory.  In general, if you need more than 200
# connections, you should probably be making more use of connection pooling.
#
# Note that by default 3 connections are reserved for autovacuum and
# administration, and more may be used by replication.

  max_connections = 100  # small server
# max_connections = 500  # web application database
# max_connections = 40   # data warehousing database
...

つっつきボイス: 「MySQLだと、configはmy-huge.cnfあたりから引っ張ってきますね」「MySQLはソースからインストールすると/sharedの下にこの手の設定済みファイルがどっさり入ってくるんで、その中から環境に一番合うファイルを探して使うと: こういうのは自力で設定するもんじゃないw」

⚓pgDash: PostgreSQLを詳しく監視する


pgdash.ioより

⚓JavaScript

⚓consola: ちょっとカッコいいコンソールロガー(GitHub Trendingより)


同リポジトリより

数日で★1400超えです。


つっつきボイス: 「やけに★が多いなと思って」「JavaScriptユーザーはめちゃ多いからw」

⚓puppeteer: ChromeのヘッドレスNode API


つっつきボイス: 「これたしか社内で使ってる人いますよね」「…使ってます」「karmaにしたかったけど画面をiframeにはめたりしてネイティブで動かしていない制約があったり云々(聞き取れず🙇)」


karma-runner.github.ioより

⚓V8エンジン6.6がリリース

Array#reduceが速くなったというのがとりあえず目につきました。


同記事より

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

⚓WebSocketsって、要る?(RubyFlowより)


同記事より

⚓JavaScriptとRustをWebAssemblyで(Awesome Rubyより)

# 同記事より
#![feature(proc_macro, wasm_custom_section, wasm_import_module)]

extern crate wasm_bindgen;

use wasm_bindgen::prelude::*;

#[wasm_bindgen]
extern {
    fn alert(s: &str);
}

#[wasm_bindgen]
pub fn greet(name: &str) {
    alert(&format!("Hello, {}!", name));
}

Mozilla Hackブログの記事です。

⚓iOSでPWAする


同記事より


つっつきボイス: 「PWA知見貯めたいです」「そういえば前にPWA記事翻訳したら結構読んでもらえましたね」「PWA記事は炎上しがち」

WebサイトをPWA(Progressive Web App)に変える簡単な手順(翻訳)

⚓ブランディングのコツ(Hacklinesより)


同記事より


つっつきボイス: 「非エンジニア向けにもいいかと思って」「うん、こういうのはいいですね: サービスの開発をするんだったらこういうブランドづくりの話も理解できるようにしておきたい」

⚓その他

⚓「最近のGo」コーナー

参考: WebAssemblyとは何であり、何でないのか - yhara.jp

つながりありませんが、なぜかムラムラとここに貼りたくなったので↓。

⚓仮想通貨を勝手にマイニングするChrome拡張をGoogleが全面禁止

参考: Google、仮想通貨を採掘するChrome拡張機能を全面禁止に - ねとらぼ

⚓Linuxで楽しいのは/procディレクトリ


つっつきボイス:/proc探検はLinux初心者ならぜひやるべきですね」「そうそう: とりあえず雑にcatしたりrmしてみたり、それで何が起こっているのか見るのがオモシロイ」

Linuxの面白みを手っ取り早く知りたい方におすすめです、/proc探検。

⚓envison: ネットワーク可視化ツール(Ruby Weeklyより)


つっつきボイス: 「内容はともかく、リポジトリのトップに書かれている”red / blue team”って何だったかなと思って」「つRed team
「セキュリティ関連の書籍なんかでは、攻撃側をred team、防衛側をblue teamって呼ぶのが普通」「あ、AliceとBobとCharlieみたいな」「そうそう、セキュリティ界隈ではそう呼ぶことに決まってるので、単なる固有名詞以上の意味がある」「ちなみにこの3人は、頭文字がそれぞれABCなのがポイントですね」「カタカナに翻訳されるとわかんなくなるw」「悪いヤツはEで始まることになってるとか」「evilに通じるからですかね」

参考: レッドチーム(Red Team)とブルーチーム(Blue Team) 図解サイバーセキュリティ用語 – 図解サイバー攻撃 [Cyber Attack dot Net]

参考: Wikipedia-en Alice and Bob


en.wikipedia.org/wiki/Alice_and_Bobより

⚓番外

⚓英語の5-7-5

haml.infoのトップにある俳句のことみたいです。


同サイトより

英語はシラブルの概念が日本語とまったく違うから、5-7-5の数字に囚われずにむしろラップをベースに小節数で考えたらいいのかな。

⚓ナンプレ

ナンプレ本ってどんな田舎のスーパーの雑誌コーナーにも必ずあるのがちょっと不思議。

⚓「安定な結婚の問題」

参考: Wikipedia-ja 安定結婚問題

もしかして記憶違いかもしれませんが、だいぶ前にシャプレーのこの手法の紹介を読んだときに「4人目の異性と付き合った時点で結婚するのが最適化が最も進む」と説明されていてなるほどと思った覚えがあります。付き合う人数をそれ以上増やしてもベストマッチングには貢献しないと。

考えてみたら、一発目で最良の縁結びをしようなんてたいそう虫のいい考えかもしれませんね。大学入試や結婚もそうですが、転職などもそうなのかも。クラウドサービスなんかも4種類ぐらい試してから決める方がマッチング進むかも。

⚓放送開始、もう20年前か


つっつきボイス: 「ざわ…」「ざわ…」

⚓そろそろ脳に埋められるようになったっぽい

参考: 脳に埋め込み神経細胞と接続する電子プローブ――脳医学研究や生体信号によるロボット制御が狙い | fabcross for エンジニア

⚓デスティニー

仏滅にデプロイしてコケたときには「ああやっぱり」と自分を責めずに済み、大安にデプロイしてコケたときには「大安でも勝てぬか…」と自らを慰める効果があります、きっと。

⚓画面では今まで四捨五入してた(´・ω・`)


pc.watch.impress.co.jpより

遠い遠い昔にニュートン・ラフソン法で(確かRubyだったと思いたい)お遊び実装による平方根を求めてみたら、手動で数回ループを回すだけでびっくりするぐらい高精度な結果が出て、確かにこれなら大昔の原始的な電卓の機械語でもすぐできるなと思ったのを思い出しました。収束、速!


https://www.maplesoft.com/support/help/maple/view.aspx?path=Student/Calculus1/NewtonsMethodより


今週は以上です。

おたよりコーナー

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

週刊Railsウォッチ(20180330)春のリリースラッシュ: Rails 5.1.6/5.0.7とRuby 2.5.1など、Ruby 2.2は3月でメンテ終了ほか

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

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

Ruby 公式ニュース

Rails公式ニュース

Ruby Weekly

Awesome Ruby

Ruby on Rails Security Project

RubyFlow

160928_1638_XvIP4h

Hacklines

Hacklines

Postgres Weekly

postgres_weekly_banner

JavaScript Weekly

javascriptweekly_logo_captured

Github Trending

160928_1701_Q9dJIU


GitLab 10.6以降でpushイベントのSlack通知が止まったときの対応方法

$
0
0

先日社内のGitLabを10.7系にアップデートしたのですが、git pushしてもSlackに通知が来なくなってしまいました。

↑ Issueの通知やMerge Requestの通知は届くし、SandStarさん(社内CIサーバ)も元気に動いている。

↑ 設定も変えてないしちゃんとONになっている。

おかしいなあと思ってソースを追いかけてみたところ、

この修正が理由でした。

ということでこの「Notify only default branch」チェックを外したら期待通りに動作するようになりました。元の動作がバグだったんですね。

社内事情で10.6系を飛ばして10.5系→10.7系にしていたのを忘れて、10.7のCHANGELOGや最近のIssueをひたすら探して見つからず遠回りしてしまいました。

関連記事

チーム開発においてGit初心者が踏みがちな地雷まとめ

WebデザイナーがGitを始めて大変だった・めっちゃ良かった話

Stripe決済を自社サービスに導入してわかった5つの利点と2つの惜しい点

$
0
0

弊社で提供している塾シリーズ(入退室管理システム『入退くん』コマ組み管理システム『コマグミくん』)がクレジットカード支払いに対応しました。決済代行会社としてStripeを利用したので、実際に使ってみて気づいた点を書いてみます。


https://stripe.com/about/resourcesより

バックグラウンド

私がこれまで実際に使ったことがある決済システムは、GMOペイメントゲートウェイソフトバンクペイメントGMOイプシロンエフレジなどです。いわゆるAPI型サービスはサンプルやドキュメントを眺める程度で、実際に使うのは初めてでした。

SPIKE, Pay.jp, omiseなども比較検討しましたが、決済においては規模や実績が大事だと判断し、Stripeを採用しました。

Stripeの良かった点

1. 導入までが早い

API型全般にいえると思いますが、圧倒的な短期間で使い始めることができます。テストアカウントはもちろん、本番決済まで1日でできそうでした。

アカウントの発行は、Webからぽちぽちやるだけです。紙の書類はもちろん、メールでの自然言語によるやりとりすらなく、機械的に登録が完了するのは非常に楽です。伝統的な決済代行に対するもっともわかりやすいメリットの一つですね。

本番利用には、以下のような情報が必要ですが、これもフォームで入力するだけでよく、登録後即座に利用できました(おそらく、審査NGだとあとからアカウントが保留になるのだと思います)。

  • 会社の登記番号(ググれば探せます)
  • 責任者(代表または事業責任者)の身分証スキャン
  • 電話番号
  • サービス名、サービスの概要、特商法URL
  • 支払いを受け取る銀行口座情報

開発に必要なテストアカウントは、ほぼメールアドレスだけで発行できます。

2. 隠れた手数料が本当にない

導入が簡単、と並ぶAPI型の大きなメリットの1つとして、手数料が安いことが上げられます。固定費ゼロで料率も3.6%と良心的です。

最初に受け取り銀行口座を登録する際、名義人のカタカナ表記がわからず、エラーになってしまいました。この場合登録情報を更新して再度振り込みを受けることになりますが、このような時でも手数料はかからず、振り込み手数料無料で受け取ることができました。
Stripe側で無駄な手数料が発生したはずで申し訳ない気持ちですが、隠れた手数料がないのは安心して使えますね。最低支払額もなく、数百円であってもちゃんと週ごとに受け取ることができます。

3. 開発工数を抑えられる

基本決済以外のすべてをダッシュボードに寄せられる

ダッシュボードの完成度がとても高いです。支払い履歴の検索はもちろん、返金作業、登録済み顧客への臨時の請求をダッシュボードから行うこともできます。

返金や一時的な金額変動はどうしても発生しますが、小規模なシステムでそのための機能を実装・テストするのは大変です。このダッシュボードがあるおかげで、例外運用はすべてダッシュボードに寄せ、顧客に直接見せる部分やシステム連携が必要な部分だけを実装し、すぐにサービスインすることができます。例外運用を自動化する実装はサービスが大規模になってきてからで良いので、初期開発コストを大幅に圧縮できます。

※ダッシュボードからクレジットカード番号を入力することもできるので、究極的には一切開発せずダッシュボードだけで決済できますが、これをやるとPCI DSSの自己問診範囲が増えると思います。

自動レシートが便利

決済完了時に、顧客に自動レシートメールを送ることができます。カスタマイズの幅は少ないのですが、領収書メールやPDF発行機能を自前実装するとそれなりの工数になるため、よく使う機能が標準搭載されているのはとてもありがたいです。

※なお、この機能はたまにふと英語になったり、改行が反映されなくなったり、若干安定しないようです。

4. 簡単にセキュアなシステムを構築できる

トークン型を実際に使ったのは初めてだったのですが、標準のポップアップ型の他に、自分でレイアウトを定義する方法でも入力フォームがiframeで隔離されるので安心でした(これはどのサービスでもそうなっていると思います)。

真の意味での非保持化(たとえ自社側のサーバプログラムやJavaScriptが悪意を持っていたとしても、ユーザの入力したカード番号は隔離されたiframe内で完結するためアクセスできない)がここまで簡単に実装できるとは良い時代です。

このような見た目でも、input要素1個1個がiframeで囲まれており、セキュリティが保たれる

また公式の開発ドキュメントライブラリが充実しており、React用のコンポーネントなどもしっかり用意されています。決済サービスで非公式のライブラリを使うのは怖いので、公式が充実していることはとても大切です。

5. 日本語でサポートが受けられる

サポートはメール対応で、概ね1-2営業日程度で返信がもらえました。なんちゃってじゃぱにーずではなく、完璧な日本語サポートが受けられます(数回の問い合わせで、日本語に違和感を感じたことは一度もありません)。ややこしい質問にもしっかり対応していただけて、サポートのレベルは高いと思います。

お金が絡むだけに、いざというときにしっかりとサポートが受けられるのは大切ですね。

※ダッシュボードはごく一部英語が残っていますがおそらく英語アレルギーの担当者に触らせることは問題ないレベル、開発ドキュメントは全て英語です。まあ開発ドキュメントを半端に日本語化されても逆に不便なので問題ないですね。

Stripeの残念な点

1. JCBが使えない使えなかった

最初からわかっていたことなので取り立てて言うことでもないのですが、JCBは使えません。
なおUSD決済なら使えます。USD圏内でJCB保持者はどのくらいいるんですかね。

追記: ごく最近(2018/05/17)、StripeがJCBカード決済に対応すると発表がありました。

参考: オンライン決済「Stripe」、JCBのカード決済に対応--グローバルで提携 - CNET Japan

2. 引き落とし明細の表記をコントロールできない

これが今回一番困った点でした。

テスト環境で問題なかったので本番カードで決済してみたところ、明細書を見ると「オンライン決済」の表記が…

三井住友VISAだと「オンライン決済」となり備考欄に登録した表記が出る
ヨドバシカードなどだと、備考欄にも出ず、何の決済かわからない

手持ちのクレジットカードで確認したところ、以下のようになりました。

「オンライン決済」と表示されるもの

  • 三井住友VISAカード
  • Amazonマスターカード
  • ヨドバシゴールドポイントカードプラス

「ST* 登録したサービス名 」と表示されるもの

  • ビックカメラViewスイカカード
  • ソニー銀行デビットカード

問い合わせた結果、一部のクレジットカード(VJAグループなど)ではこのような表記になるそうです。その「一部」に三井住友VISAカードが含まれています。おそらく国内でかなり多くのカードが該当するため、要注意です。

なお、「カード会社によっては ST* のプレフィックスが付与されることもある」とのことですが、現在のところ、付与されないカードを見つけたことはありません。

まとめ

開発者とスタートアップに優しいサービスだと思いました。API型全般に言えることですが、直接的な手数料のほか、開発コストを圧縮できるのは大きなメリットです。特に、例外系の運用を最初から実装しなくて良いのが素晴らしいです。

一方、日本で本格的に使うサービスとしてみると、やや荒削りな面があるのも事実です。

pay.jpなども試してみたいところですが、あまり小規模なサービスはwebpayのこともあり怖いので、Stripeが日本市場で人気を博し、早く成熟してくれるのが一番です。ということで皆さんStripeをどんどん使っていってください。

追記: ツイートより

関連記事

タイムカード置き換え型、勤怠管理稼働集計システム「入退くん」もリリースしました

2017年リリースの自社サービスを携えて「2018 塾・教育総合展」に出展いたします!

週刊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の機能じゃなかったっけ?」

毎度のことですが、記号は検索しづらいです(リンクを見つけるのに30分近くかかりました😭)。マイナス記号-です。

参考: 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が入るようになったんだっけ?どっちだったっけ?」「えーと」

そういえばRuby 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が「グルジア語の」とも「ジョージア(州)の」とも読めてしまうので、いちいち断りが必要です。グルジアというとスターリンの出身国というイメージ。なおグルジア語とロシア語には共通部分がほぼありません。


Kartvelian languages - グルジア語 - Wikipediaより

参考: グルジア語 - 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定めたいと思ってる🤓


github.com/rubocop-hq/rubocopより

⚓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だ。

参考: ラムダ計算 - Wikipedia


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


ramdajs.comより

トランスデューサーそのものはめちゃめちゃ意味が広いですが、ここでは関数型言語よりの何かなんでしょうね。何も見ずに「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ですからね🕶


github.com/piotrmurach/ttyより

⚓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を含める感じではなさそう…なぜかこの方面はさっぱり発展しません(´・ω・`)。

⚓http.rbはいいぞ(Ruby Weeklyより)


同リポジトリより

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

つっつきボイス: 「このhttprb/httpってどうでしょう?」「少なくとも表の青いところにあるRuby標準の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だって: このgem名、よく取れたなー😲」「Rubygems.orgで決めてるんでしたっけ?」「早いもの勝ちです😎


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試験になら出そうですね…😓

追記(2018/06/17)

まったくの偶然ですが、フリップフロップが次から消えることになりそうです。

⚓その他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


つっつきボイス: 「ちなDonald Norman氏の今や古典とも言うべき誰のためのデザイン?は、主に工業デザインを扱っていますがITエンジニアも必読ですね🧐: おそらくですが、学校でデザインを学ぶ人はきっとどこかでこの本を読む機会があるんじゃないかな」「😃

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

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

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


testerchan.hatenadiary.comより

⚓Apollo Clientとは


apollographql.comより

⚓言語よろずの間

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


同リポジトリより

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

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


play.folang.orgより

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

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

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

毎度お騒がせ。好きな言語1位がRustという。


insights.stackoverflow.comより


つっつきボイス: 「想像以上にめちゃ長い…」「呑みながらつっつくのによさそう🍶

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

⚓その他言語

参考: 依存型 - Wikipedia
参考: 直観論理 - Wikipedia — 依存型の基礎となる論理で、古典論理といくつかの点で異なります。





⚓その他

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

⚓らめぇそれ

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

はてブで知りました。

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

⚓その他のその他





社内勉強会のお題が「トランザクションとロック」だったので、そういうときについ思い出す動画です。トランザクションとロックを人力でやってた時代は、特急券の指定券発行に30分かかってたそうです🚆

⚓番外

⚓今どきの法科学

↑女性です。

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


つっつきボイス: 「これはねー、手の細胞が全部剥がれ落ちるまで辛抱強く待つしかない😇」「😇

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

⚓香害

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

リンネが創始した生物の近代的な分類学は結局外観をベースとしていたために、特に遺伝子生物学が登場してからというものアドホックな修正やら論争やらが当分止みそうにありません。昔なら「葉緑体を持っていれば植物」でしたが、もうそんな素朴な分類ではどうにもならなさそう。分類学って人間の都合でしかないんだなと痛感します。

参考: カール・フォン・リンネ - Wikipedia
参考: 生物の分類 - Wikipedia

⚓火星で有機物発見か

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

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


今週は以上です。

おたより発掘

バックナンバー(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

G Suite: 社内ファイルサーバからチームドライブ移行時の注意点と対策

$
0
0

これまで独自で運用していたメールサーバ、ファイルサーバや個人Googleアカウントで利用していたGoogleサービスを、G Suiteに移行しました。色々と想定外の苦労もあったので、これまでに得られた知見を何回かに分けて記録していこうと思います。

今回は、SambaファイルサーバをGoogleドライブ(チームドライブ) へ移行する話です。40人程度の小規模な会社なので、まさかパフォーマンスや容量の問題は無いだろうとたかをくくっていたら意外と苦労しました。

結論

  • チームドライブや「ドライブ ファイル ストリーム」アプリの登場により、以前よりもGoogle Driveを共有フォルダとして使いやすくなっている。
  • 実際に使うと、機能制限共有設定速度の面でそれぞれ大きな落とし穴がある。
  • Samba共有の感覚で運用を変えずそのまま移行するのはほぼ無理だが、わかった上で運用を工夫すれば代替は可能。

G SuiteとGoogleドライブの紹介

最初に、G SuiteやGoogleドライブの機能概要のおさらいです。

個人GoogleアカウントとG Suiteアカウント

通常Googleアカウントを新規登録(無料)すると作成されるものを便宜上「個人Googleアカウント」と呼ぶことにします(正式な呼び方は知りません)。一般的にはGmailアドレスとともに発行しますが、すでに所有しているメールアドレスで発行することもできます。

一方、組織のG Suiteで発行したアカウントは、組織のドメインのメールアドレスと関連付けられます。このアカウントは組織によって管理され、アカウントの停止やパスワード再発行は組織管理者が行うことになります。

マイドライブとチームドライブの違い

Googleドライブには、「マイドライブ」と「チームドライブ」の大きく分けて2つがあります。


1. マイドライブ

個人のGmailアカウントでも使える、一般的なGoogleドライブとして認識されているものです。以下の特徴があります。

  • ファイル作成者がオーナーになる。
  • オーナーのドライブ容量を消費する。共有された他ユーザのドライブ容量は消費しない。
  • フォルダ単位での共有もできる。
  • オーナーは、共有中の別ユーザにオーナーを移転できる。
  • オーナーのGoogleアカウントを削除すると、ファイルは消滅する。

元々はこのマイドライブしかなかったのですが、組織で使う際に退職者アカウントを削除するとファイルが消える、またオーナー自身しかオーナーを移転できないため、連絡が取れないユーザのオーナー権を移転できないと言った問題がありました。

これに対応するため追加されたのがチームドライブです。


2. チームドライブ

G SuiteのBusinessプラン以上(Basic不可)でのみ使える機能です。以下の特徴があります。

  • チームドライブそのものがオーナーになる(作成者を問わない)。
  • 組織全体のドライブ容量を消費し、各ユーザのドライブ容量は消費しない。
    • ただしチームドライブを使えるのはBusinessプラン以上(5名以上で容量無制限)なので、コスト面への影響は少ない。
  • チームドライブ単位やファイル単位の共有はできるが、フォルダ単位の共有はできない
  • 作成はG Suite Businessプラン以上が必須だが、個人Googleアカウントに共有することは可能。

チームドライブ内に作ったファイルはすべてチームドライブがオーナーとなるため、ファイル作成者のGoogleアカウントを削除してもファイルが消えないのが特徴です。

Web UIとクライアントアプリ

GoogleドライブというとWeb(やスマートフォンアプリ)でアクセスするイメージが定着していると思いますが、PC用アプリも用意されています↓。


1.「バックアップと同期」


google.comより

昔からある「Googleドライブ」アプリの後継です。

  • PCにファイルを同期する、昔ながらのDropbox的なアプリ。
  • 個人GoogleアカウントでもG Suiteアカウントでも使える。
  • マイドライブにのみアクセス可能(チームドライブへはアクセス不可能)。
  • マイドライブ全体、または選択したフォルダのみを同期できる。
    • マイドライブに大量のデータを置いておりPCに入りきらない場合、よく使うフォルダだけをノートPCに同期するなど。
  • PC内のどこに同期するかを指定できる。たとえば D:\Google\MyDrive のように任意のパスを指定できる。
  • PCのデスクトップ等、指定したフォルダをバックアップできる。

2.「ドライブ ファイル ストリーム」


google.comより

G Suiteアカウントでのみ使えるアプリです。

  • ファイルを開くときにオンデマンドでダウンロードするので、SSDの小さいPCでも全データを同期できる。
    • Dropbox Businessのスマートシンクなどと同様。
  • 指定フォルダのみオフライン用に同期することも可能。
  • マイドライブおよびチームドライブにアクセス可能。
  • 常に専用のドライブレター(デフォルトは G: )が割り当てられる。任意のフォルダへのマウントはできない。
  • 個人Googleアカウントでは一切利用できない

注意点 機能編

ようやく本題です。実際に運用してみると、いくつかはまりどころがありました。

1. チームドライブのフォルダ共有ができない

チームドライブは、全体を共有するか内部のファイルを1個ずつ共有することしかできません。例えば「第1開発部」というチームドライブを作り、その中の「プロジェクトA」フォルダのみをアルバイトに共有する、といったことは不可能です。

公式のガイドラインにもある通り、ドライブ内の全ファイルが同じメンバーからアクセスできるべき、という単位でチームドライブを作る必要があります。この場合でいえば、「第1開発部」というチームドライブは不適切で「プロジェクトA」というチームドライブを作るべきでした。

2. 個人GoogleアカウントでPCアプリを使いたい場合、チームドライブは利用不可

アルバイトや外部の協力会社に対して、G Suiteアカウントは手間やコストの関係で発行せず、個人Googleアカウント(gmail.com)で共有することもあると思います。この場合、そのアカウントではドライブ ファイル ストリームが使えず、バックアップと同期のみが利用可能です。つまり、デスクトップアプリからチームドライブにアクセスすることはできません。もしPCに同期しないと使いづらい運用をする場合(作業フォルダなど)、チームドライブを使わないという選択肢を検討する必要があるかもしれません。

3. チームドライブの共有権限がへんてこ

チームドライブでは「すべて」「編集可能」「コメント可能」「閲覧のみ」の権限設定がありますが、なぜか「編集可能」ではファイルの削除や移動ができません

参考: Get started with Team Drives – Google Learning Center

さらに、ドライブ ファイル ストリーム経由でアクセスしている場合、「編集可能」権限では閲覧しかできません。つまり、共有フォルダ代わりに使うには、全員に「すべて」権限を与えるしかないということになります。

これに関しては不満も多いようで、最近「すべて」権限でもユーザを追加できないようにする管理者設定が追加されました。どうしてこうなった…

これをONにしてしまうと本当に全メンバーの管理を管理者が行わなくてはならなくて非効率です。通常のマイドライブと同じように編集可能権限が編集できればそれで良かったのです。機能リクエストは上がっているのでそのうちに実装されるかもしれません。

4. チームドライブへの保存ファイル数に制限がある

「容量無制限」を売りにしているG Suite Businessプラン以上ですが、実際にはチームドライブへの保存ファイル数に大きな制約があります。

G Suite管理者ヘルプの「チームドライブの制限」に記載がありますが、1ドライブにつき40万アイテムフォルダ20階層までしか作れません。

先月まで25万アイテムだったのですが、しれっと増えていました。なおこのページは簡単にはたどり着けないうえに、増えたというアナウンスも公式ブログですら行われない秘密主義のようです。

想像以上に少ないですよね。手元でSamba共有フォルダを数えたら2000万ファイルほどあったので、分割は必須でした。「チームドライブ」という名称ですが、事実上はより細かい「プロジェクト」などの単位で分割するのが現実的でしょう。1つだけ、1プロジェクトで200万ファイルを超えるフォルダがあったので、諦めてprojectA-2018のような名付けをしました。

ちなみに、チームドライブ内に今何個のファイルがあるかを数える簡単な手段は存在しません。ひどい。ドライブ ファイル ストリームで右クリック→プロパティを出して、表示が落ち着くまでコーヒーを淹れてくるしかないようです。

※チームドライブ数には上限がないそうです。ただし、現時点ではチームドライブ一覧はフラットに表示されるのみで、分類やスター付けもできないので、数千数万といった単位で作ると使いづらいでしょう。数百~1000位にしておいた方が良いと思います。ということは、チームドライブで数億以上のファイルを扱うのは辛みがある、ということになります。

なお、試したことはありませんがサポートによるとマイドライブにはこの制限がないとのことです。ただし、だからといってマイドライブをファイル数制限のないチームドライブ代わりに使うことは絶対に避けたほうが良いです。本記事の後半に記載の通り、致命的なパフォーマンス上の問題が発生します。

5. ドメインが違うユーザへオーナー権の移行はできない

Googleドライブファイルのオーナー権移行は、同一ドメイン内のユーザにのみ可能です。@gmail.com から @gmail.com への移行は可能ですが、 @gmail.com から @bpsinc.jp やその逆はできません。

つまり、これまで @gmail.com で運用していたGoogleドライブを組織のG Suiteに移行するのは困難が伴います。基本的な対応は2つあります。

1. コピーする

URLやIDが変わって良ければ、コピーするのが一番お手軽です。これは、移行先(G Suite)アカウントを旧ドライブに招待し、移行先アカウントで「コピー」するだけです。これで移行先アカウントのマイドライブにコピーされるので、あとはドメイン内で好きに移動すればOKです。

2. チームドライブを経由して移動する

一手間かけることで、IDを維持したままファイルを個人アカウントDriveからG Suiteアカウントに「移動」できます。

  1. まず、移行元(個人Googleアカウント)であるオーナーをチームドライブに招待します。
  2. オーナー側でファイルをチームドライブに「移動」します。これでオーナー権限がチームドライブに移行されます。

この操作のみ、ドメインをまたいでオーナーを移動できないというルールの例外のようです。

一度移動したら、すでにそのファイルはG Suiteドメインの所有なので、G Suiteドメイン内で自由にオーナー移行(チームドライブからG Suite内のマイドライブに移動するなど)が可能です。

なお、この操作は不可逆です。一度移動したら、二度とそのファイルを元のオーナーに戻すことはできません(組織ドメイン→gmail.comへの移動ができないため)。

6. マイドライブの一括オーナー移行はできない

マイドライブのフォルダを、すべて別オーナーに移行することはできません。フォルダのオーナーを変更はできますが、この操作はあくまでそのフォルダのオーナーを変更するのみで、内部のフォルダやファイルのオーナーは変更されません。

※G Suite管理下のアカウントでのみ、ユーザAの所有する全ファイル・フォルダをユーザBにオーナー移行するといったことは管理コンソールから可能(ユーザAが退職する前にやるべき処理)ですが、後述の通りこの操作はものすごく遅いです。

なおG Suiteドメイン内では、以下の操作は可能です。ただし、同じくとても遅いです。

  • G Suiteアカウントのマイドライブから、チームドライブにフォルダごと移動することはできる。
  • チームドライブから、G Suiteアカウントのマイドライブにフォルダごと移動することはできる。

それ以外では、フォルダをまるごとチームドライブに移動したり、別ユーザにオーナー移行する機能はありません。つまり、これまで個人Googleアカウントで使っていたフォルダをまとめて移行するには、APIやサードパーティー製のツールを使うなど工夫する必要があります。サポートに聞くと「一度ダウンロードして再アップしろ」と案内されますが、当然URLは変わりますし、DocsやSpreadsheetなどがいったんOffice形式に変換されるため、データが一部変わってしまいます。

かといってG Suite Marketplaceに置いてあるアプリは人気のものでも驚くほど出来が悪い/セキュリティを考慮していないものも多いので、あまり頼りたくない…

注意点 パフォーマンス編

1. たくさんのファイルを扱うとドライブ ファイル ストリームが重くなる

多数のファイルをアップロード/ダウンロードすると、ドライブ ファイル ストリームがものすごくCPUを食うようになります。このような場合は、 %LOCALAPPDATA%\Google\DriveFS 配下にある metadata_sqlite_db が肥大化していないか確認しましょう。手元の環境では、2GBを超えたあたりからだんだん重くなりました(6GBを超えても重いだけで動作はしました)。

この場合、一旦ログアウトしてログインし直すとファイルが消えて元の速度に戻ります。たまにやると良いかもしれません。

2. ファイル数が多いとアップロードがとても遅い

よく見かける「クラウドストレージの速度比較」のようなサイトで、Googleドライブは上位に位置することが多いようです。実際アップロード・ダウンロードの速度は高速で、100Mbps以上出ることも珍しくなく、時間帯を問わず50Mbps以上で安定している印象です(正確に測定はしていません)。

ただしこれは、1つの大きなファイルの場合の話で、ファイル数が多いと事情が異なります。Googleドライブは一度に10個程度しかアップロードできず、また1ファイルアップロードするごとに5~10秒ほどのインターバルがあるため、1KBのファイルを1000個アップロードするのは1MBのファイルを1個アップロードするのに比べて圧倒的に長い時間がかかります。(手元で大小入り乱れた100万ファイル程度のフォルダをアップロードするのに数週間かかりました)。

Sambaファイルサーバには、例えばリポジトリを展開して .svn.git が残っているケースもあるでしょう。また、 .DS_StoreThumbs.db が混じっていたり、そこまでいかなくても多数のテキストファイルやアイコン画像があることは珍しくありません。Googleドライブにこれらのファイルをアップロードするのは大変なので、古いプロジェクトについては適当にzipやtar.gzに固めるのが良いかもしれません。

※zipだとWeb UI上でダウンロードせずにプレビューできるので便利です。

これだけで、GoogleドライブはSambaファイルサーバの直接の代替にはならないことがよくわかります。

3. ドライブ ファイル ストリームのアップロードが遅い

特に帯域制限をかけていないのに、1つの大きなファイルでもアップロード速度が30Mbps程度で頭打ちになることがあります。この場合、以下を参考に BandwidthTxKBPS を明示的に大きな数字にすると、速くなることがありました。

参考: G Suite管理者ヘルプ ドライブ ファイル ストリームを構成する

4. マイドライブの共有設定は反映がとても遅い

マイドライブのフォルダにたくさんのファイルがある場合、そのフォルダを別ユーザに共有すると、反映にとても長い時間がかかります。手元で200万ファイル程度が入ったフォルダを5ユーザに共有したところ、反映完了まで1ヶ月半ほどかかりました

具体的には、マイドライブでフォルダを共有した場合、内部の各ファイルについて「共有権限設定: ユーザAを編集可能に」といった単位で設定がされていくので、200万ファイルのフォルダを5ユーザに共有すると内部的には1000万回の操作が発生するイメージになります。この操作はどうやらアイドル時に少しずつ行われるようで、ドメイン内でGoogleドライブをあまり使っていない時は1分間に200オペレーションほど進む(想像の30倍は遅い)のですが、ドメイン内でアクティブにDriveが使われている時間帯は一切進まないといった感じになります。

さらに悪いことに、この操作は中断することはできず、しかも進捗率を知ることもできません(管理コンソールの監査ログで最近どのような操作がされたかはわかるものの、全体件数や順番がわからないので、忙しいのか暇なのか位しかわからない)。つまり、うっかり大量ファイルの入ったマイドライブフォルダを共有かけてしまうと、ドメイン全体でドライブが遅くて不安定になったままいつ直るかもわからず我慢し続けるしかないことになります。

↓ドメイン全体の共有進捗状況です。速度の傾向が見えてきます。

幸い上記の200万ファイル共有は本格移行前に始めたので、まだ影響は小さかった方ですが、それでも完了までは以下のような不具合が頻発しました。

  • 新規でチームドライブを作ってしばらくの間(10日間程度)、チームドライブの名前が反映されず「チームドライブ」という名前になる(下の添付画像)。
  • チームドライブをメンバーに共有してからそのメンバーに見えるまで、3日ほどかかる。
  • マイドライブにアップロードしたファイルが消えたように見え、3日ほどたつと突然出てくる。その間に消えたと思って再度アップしたファイルも別途残る(バージョン管理されない)ため、大量のファイルが2個ずつ残る。
  • チームドライブを空にしたのに消せない状況が1ヶ月以上続く。

ものすごく不具合としか思えないですが仕様とのことです。大量のファイルが入ったフォルダを共有してはいけません!ゼッタイ!

対策

上記の通り、マイドライブでたくさんのファイルを共有すると、致命的な問題が多発します。緩和策として、以下のような運用上の工夫が可能です。

  • チームドライブを使う: チームドライブでの共有設定はトップレベルにのみ行われるので、ファイルが多数あっても反映が早い。
  • グループを使う: ユーザ5人に共有するのではなく、ユーザ5人が入ったGoogleグループを作り、そのグループに共有する。

なお、AODocsさんのサイトにより詳しいナレッジが載っていました↓。hachi833に翻訳してもらったので、こちらも併せてご覧いただくと役立つかと思います。

Googleドライブのパフォーマンスを低下させない推奨設定 — AODocs KB(翻訳)

まとめ

容量無制限に惹かれて導入したG SuiteのGoogleドライブですが、想像以上にパフォーマンス上の問題が大きかったです。試行錯誤の末、当面は以下の運用ルールにしてみました。

  • マイドライブは少数のファイルや少数の共有メンバーの場合、また一時的な用途を中心に利用する。正式なプロジェクトでは原則チームドライブを利用する。
  • プロジェクト単位でチームドライブを作成する。この際 project-XXX というようにprefixをつけることでソートしやすくする。
  • 雑多なものを入れる用途に project-その他team-XXX のチームドライブも作る。
  • プロジェクト以外の共有用に BPS-勉強会 などのチームドライブも作る。
  • チームドライブの内のファイル数が100~10万程度になるくらいの粒度を心がける。チームドライブ数は数年で数百程度になるのを想定する。
  • ZIPを展開したファイル群やリポジトリなどをドライブに置くことはなるべく避ける。

関連記事

Googleドライブのパフォーマンスを低下させない推奨設定 — AODocs KB(翻訳)

Google Driveのファイル共有状況を一括出力するgoogle-drive-permission-searchを作った

Googleスプレッドシートのセル内に画像をぴったり表示する方法

[速報] 待望のLayoutNGがやってきた!Chrome 69は見た目も中身も大幅に刷新

$
0
0

2018年9月にリリース予定のChrome 69では、見た目が大幅に変わりそうです。

見た目の変更

既にCanaryではUIが新しくなっていることに気づいた方も多いでしょう。

Chromeの特徴だった山型のタブではなく、よくあるタブの見た目になりましたが全体的に丸っこいです。

タッチパネルへの最適化などが考慮されているようです。なお、 chrome://flags#top-chrome-md フラグを設定すると、見た目を変更できました。

UI Layout for the browser’s top chrome
Toggles between 1) Normal – for clamshell devices, 2) Hybrid (previously touch) – middle point for devices with a touch screen, 3) Touchable – new unified interface for touch and convertibles (Chrome OS), 4) Material Design refresh and 5) Touchable Material Design refresh. Enabling #upcoming-ui-features forces the Material Design refresh option. – Mac, Windows, Linux, Chrome OS

top-chrome-md

▼normal

▼hybrid

▼refresh

▼touchable

▼touchable-refresh

LayoutNG

長らく待望されていたLayoutNGが、バージョン 69.0.3482.0 からとうとう試せるようになりました。Canaryで #enable-layout-ng フラグをセットすると今日から使えます。

LayoutNGについては以下のスライドも参考になります。要するに内部的に大きな刷新がされて、高速化とともに拡張性が増しているという感じですね。

レイアウト崩れのバグレポをしても「LayoutNGで直るからWontFixな」と言われてそいつは一体いつ出るんだよとやきもきしていたのは僕だけではないでしょう。期待の大型アップデートです。

なお、ちなみに普通にブラウジングしていて気づくような見た目の変化は基本的にありません。また現時点ではrubyなどのレイアウトは旧来のままになっているようです。

体感できるところでは、読めない言語(ペルシア語?)のWikipediaで、レイアウトが大幅に高速化しているとのことです。

※スクショを取り始めた段階(2018/07/05昼頃@JST)ではCanaryにLayoutNGが来ていなかったのでChromiumのスクショになっていますが既にCanaryで試せます。

(ツイートより)

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

$
0
0

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

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

⚓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を配置してそちらで動かすことになります

追記(2018/09/11): ActiveJobにはset(wait: 1.week).perform_laterなどのメソッドがあり、Sidekiqも対応しているというご指摘をいただきましたので訂正します。ありがとうございます。
参考: Active Job の基礎 | Rails ガイド — 3.2 ジョブをキューに登録する

「で今回の修正はというと、ジョブのリトライ時などにフックをかけられるようにしたということのようですね」「…だいたい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でできます: 本来単なる非同期ジョブとスケジューリングは別の話ですし」</del「スケジューリングとかは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のテストコードがちゃんと動いているかどうかが気になってくると本末転倒感ある🧐

⚓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年度後半)

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

ソースの表記されていない項目は独自ルート(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

週刊Railsウォッチ(20181119)レビューでチェックされる非効率な書き方、Local Storageは使うなほか

$
0
0

こんにちは、hachi8833です。Twitterで流れてきたAmazon Payのエラーページを二度見してしまいました。


つっつきボイス:「Amazon Payで何ができるのかがそもそも自分が知らないという😆」「やっぱりAmazonでペイするんでしょうね😆」「Railsでさっと作って立ち上げる、王道の使い方🤴

参考: Amazon Payで簡単で安心なオンライン決済を | Amazon Pay

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

⚓お知らせ: 週刊Railsウォッチ: 公開つっつき会#5

次回の公開つっつき会は12/6(木)に開催いたします。定員を10名に増やしましたので、皆さまのお気軽なご応募をお待ちしております🙇

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

公式の更新情報はなかったので、コミットから見繕いました。

⚓パラメータのparseエラーをrescueできるようにした

# actionpack/lib/action_controller/metal/params_wrapper.rb#L243
     # Performs parameters wrapping upon the request. Called automatically
     # by the metal call stack.
     def process_action(*args)
       if _wrapper_enabled?
         wrapped_hash = _wrap_parameters request.request_parameters
         wrapped_keys = request.request_parameters.keys
         wrapped_filtered_hash = _wrap_parameters request.filtered_parameters.slice(*wrapped_keys)

         # This will make the wrapped hash accessible from controller and view.
         request.parameters.merge! wrapped_hash
         request.request_parameters.merge! wrapped_hash

        # This will display the wrapped hash in the log file.
        request.filtered_parameters.merge! wrapped_filtered_hash
      end
-     super
+   ensure
+     # メモ: すべての例外をrescueするので、
+     # ActionController::Rescueでキャッチされるであろう
+     return super
    end

つっつきボイス:「strong parametersのお話なのかな?」「うーん🤔」「RescueよりParamsWrapperの方が後にincludeされてるから、PramsWrapperでraiseしてもRescueにキャッチされない状態だったんですね」「やや、なるほど!」

「お、改修箇所↑のprocess_actionensureしてからreturn superしてるのが気になる👁: こういう書き方ってアリ?🐜」「むむ?」「このparams_wrapper.rbを見てみると他にもsuperだらけですね」「これはラッパーなんでsuperいっぱいなのは不思議ではない🤓

「しかしエラーになってもならなくても常にreturn super…?: エラー握りつぶしだとしたら一般には悪手っぽいんだけどな〜😅」「Rubyスタイルガイドをチェックするとensureブロックでは結果を返さないこと」という記述がありますね↓」

ensureブロックで返した結果は、raiseされる例外より優先されてしまいます。これでは例外が発生してない場合と区別できなくなってしまい、例外が警告なしで捨てられてしまいます。

Rubyスタイルガイドを読む: 例外処理

「そう、業務コードならensurereturnするもんじゃない: けどこれはフレームワークのコードなんで、ちょっとトリッキーではあるけどensurereturn superすることには意味がある」「そっか、常に最後にsuperすることが前提だからか」「superは必ず実行したいけど、例外も吐きたい、でも例外を吐いたときにスーパークラスのprocess_actionが呼ばれないのは困ると」「ふんわり見えてきた気が🙄」「やりたいのはensuresuperすることで、returnは書かなくてもいいような気はするんだけど🤔、あ、成功時にsuperの結果を返したいってことかな?」

「わからなくなってきたので、ちょっとensureの挙動をチェックしたろ↓」

def hoge
  'hoge'
ensure
  42
end

hoge #=> hoge
def huga
  'huga'
ensure
  return 42
end

huga #=> 42

「おう、returnしないと正常値のhogeが返るけど、returnするとhugaが出なくなるし」「ということは」「結局さっきの改修は、正常時にはsuperの結果を返し、エラー時にもsuperの結果を返すようにすると: スゲー書き方🤓」「何だかスゲー」「だから明示的にreturnを書かないといけないのか: そしてsuper呼ぶ前にmerge!やら何やら破壊的な前処理やってるんで中断すると困っちゃうヤツ」

beginendrescueして書く方法もありそうだけど階層も深くなるし冗長になるし、だからこう修正したんじゃないかな🤔」「普通ならRubocopには怒られるヤツでしょうね」「superと例外の両方の通り道があるのがややこしい😅

フレームワークならではの書き方で以下の記事↓を思い出しました。

Railsフレームワークで多用される「options = {} 」引数は軽々しく真似しない方がいいという話

「ところでこのParamsWrapperが置かれているmetalってのも気になります」「きっとRailsのルーティングエンジンがjourneyと呼ばれてたみたいなニックネームなんでしょうけど」「お、このステキな記事↓にRails Metalは『Rails 2.3 でcgiからRackベースに変更した際に抽象化レイヤとして導入された』って書いてあるし」

参考: Rails考古学:WebAPIを取り巻く環境の変化とRailsの対応 - Qiita

「つかRails 2.3でRackが導入される以前ってCGIベースだったんですか😳!」「ってことなんでしょうね〜: そういえば当時Passenger使ってた気がするんだけど、あれは実はCGIだったんかな〜👨🏾‍🦳?」

参考: Common Gateway Interface - Wikipedia

「抽象化レイヤを鉄板に見立ててMetalと呼んだのかなと想像してます」「metalって抽象化レイヤというより、Action Controllerの実体が全部入っているところというイメージありますけどね☺

参考: ActionController::Metal

RailsとMetalからの連想です↓。

「しかしensurereturn書くかどうかで振る舞い変わるとは知らんかったなぁ」「コミットで他の人が誰もツッコミ入れずに当然のようにマージされてるのもスゴイですね」「ensureでだいぶ時間使ったので次へ〜」

その後こちらのツイートをたまたま見つけました↓。

⚓autoload_pathsからアセットのパスを削除

#34397修正のため、app/assetsapp/javascriptが削除されました。

# railties/lib/rails/engine/configuration.rb#L37
       def paths
        @paths ||= begin
          paths = Rails::Paths::Root.new(@root)
-         paths.add "app",                 eager_load: true, glob: "{*,*/concerns}"
+         paths.add "app",                 eager_load: true,
+                                          glob: "{*,*/concerns}",
+                                          exclude: %w(assets javascript)

つっつきボイス:「app/javascripts/foo/barディレクトリが存在していると、Autoloadの機能でFoo::Barモジュールが定義できてしまう問題のように見えますね」「おー😳」「assetsとjavascripts配下はautoloadとは関係ない(rubyは置かれない)から消したって感じな気がする」

⚓UNIONUNION ALLで余分な丸かっこが生成されないよう修正

# activerecord/lib/arel/visitors/to_sql.rb#L270
        def visit_Arel_Nodes_Union(o, collector)
-         collector << "( "
-         infix_value(o, collector, " UNION ") << " )"
+         infix_value_with_paren(o, collector, " UNION ")
        end
         def visit_Arel_Nodes_UnionAll(o, collector)
-         collector << "( "
-         infix_value(o, collector, " UNION ALL ") << " )"
+         infix_value_with_paren(o, collector, " UNION ALL ")
        end

つっつきボイス:「そもそもUNIONとUNION ALLって何が違うんだっけか🤔?」「まずUNIONがわかってない😅」「そうそう重複を含むか含まないかだった: UNIONは2つのSQLの結果をガッチャンコと合わせるヤツで、そこに重複を含めるのがUNION ALL」「そうでしたか!」「合わせる、をちゃんと言えば和集合↓」「ありゃ、UNIONの直訳😆

参考: 逆引きSQL構文集 - 複数の検索結果を統合する(和集合)

「今までUNIONとUNION ALLに余分なかっこがついてたと: 誰も使ってなかったのかな😆」「コードでもむき出しのリテラルでかっこ付けてますね」「低レイヤの処理だからそうなっちゃいますね」「ヒアドキュメントかなと思ったら<<collectorに押し込んでる」

「コミットメッセージによると、余分なかっこがついているときの挙動がRDBMSによって違ってたらしく、SQLite3でエラーになってたと」「RDBMSで挙動が違うというのはありそう」「MySQLは余分なかっこを抑制してくれると」「今回の改修でRDBMSごとの違いをなくして実装が共有されたのか: よかったよかった☺

「もともと昨年12月に#510が上がっていたのが、その後Arelがお引越ししたことでリトライしたようです」「あるある☺自分はArel使うことはないけどっ😆

⚓PostgreSQLのCREATE UNLOGGED TABLEをサポート


つっつきボイス:「CREATE UNLOGGED TABLE…?🤔」「ぽすぐれの機能みたいです」「こんな記事↓見つけたので貼っておこう」

参考: 【PostgreSQL】unloggedを試してみた - Qiita

「何のログかなと思ったらトランザクションログかやっぱり: この辺まるっきり想像ですが、ロールバックなんかしないぜというときに使うのかな?トランザクションログを出せばその分遅くなるので」「CREATE TABLEでやってるからテーブル単位で設定するのか」「このテーブルはトランザクションの中でもログは出さないと」「その分きびきび動いて欲しいんでしょうね: INSERTとかが速くなるようだ」

「コミットメッセージにはproductionで使うなと書いてますね」「あぁーそういうことか!わかる〜😋: 開発中にトランザクションログで遅くなるのはイヤだし」「でもproductionでトランザクションログ出さなかったらアウトだし」「開発中というよりはテストを速くするのに使うのかも」「あ、たしかに」「テストではどうせトランザクションログなんか空にするんだから生成しなくてもいいでしょと: ま想像ですけど😆

⚓非Unicode文字列のnormalizeテストを追加

# activesupport/test/multibyte_chars_test.rb#L
+ def test_normalize_non_unicode_string
+   # Fullwidth Latin Capital Letter A in Windows 31J
+   str = "\u{ff21}".encode(Encoding::Windows_31J)
+   assert_raise Encoding::CompatibilityError do
+     ActiveSupport::Deprecation.silence do
+       ActiveSupport::Multibyte::Unicode.normalize(str)
+     end
+   end
+ end

つっつきボイス:「マルチリンガル関連だったので」「足されたのはテストだけか」「"\u{ff21}".encode(Encoding::Windows_31J)っていうすっごくイヤらしいエンコードのnormalizeをテストしてますね」「出たWindows 3.1J😳: 日本語のエンコードはWindows 3.1JとかCP932とかShift_JISとかあって、それぞれ微妙に違うんですよねー」「私はUTF-8以外拒否したいです😆」「こういういにしえの非Unicodeエンコードをnormalizeしたらエラーを吐くべきと: こういうテストは重要ですね」

参考: Microsoftコードページ932 - Wikipedia
参考: 本当は怖くないCP932 - Qiita
参考: #normalize — ActiveSupport::Multibyte::Unicode

「closeされた#3462のコメントをみると以下のバグ↓が過去にあって、それを直したけどデグレチェックのテストがなかったのを追加した、という雰囲気」

# Fullwidth Latin Capital Letter A in Windows 31J
str = "\u{ff21}".encode(Encoding::Windows_31J)
result = ActiveSupport::Multibyte::Unicode.normalize(str)
# This `result` should A ("\u{41}") but got another char, 艠 ("\u{8260}")
puts result # => 艠
p result.codepoints # => [33376]

⚓番外: --webpack--webpackerのどちらのオプションも使えるようにした

# railties/lib/rails/generators/rails/app/app_generator.rb#L261
-     class_option :webpack, type: :string, default: nil,
-                            desc: "Preconfigure Webpack with a particular framework (options: #{WEBPACKS.join('/')})"
+     class_option :webpack, type: :string, aliases: "--webpacker", default: nil,
+                            desc: "Preconfigure Webpack with a particular framework (options: #{WEBPACKS.join(", ")})"

つっつきボイス:「小ネタですが--webpack--webpackerどっちも使えるようにしたそうです: きっとみんな間違えるでしょという感じで😆」「エイリアスだ😆: こういう修正は地味にいいですね〜」

⚓Rails

⚓開発環境向け画像プレースホルダ(RubyFlowより)

TechRachoの翻訳記事でお世話になっているArkencyさんのブログです。


つっつきボイス:「開発中に画像が存在していない箇所に例のひび割れアイコンが表示されるのを回避するということみたいです」「おーなるほど😋どんなURLにしてもNot Foundに画像を表示してくれると」「よく使われるplaceholder.com↓の画像にしてもいいし、お遊びでスティーブン・セガール画像やらニコラス・ケイジ画像にしてもいいよって😆

「絵はこの際何でもいいけど😆、こうやってサイズのわかるダミー画像↓が開発中に表示されるのはよさそう!」「まさに開発用」


placeholder.comaより

「記事の最後に、それを一発でやれるgem↓が紹介されてます」

# 同リポジトリより
# config/environments/development.rb

Rails.application.configure do
  config.middleware.use ImagePlaceholder::Middleware, size_pattern: {
    %r{/uploads/.*/s_[0-9]+\.[a-z]{3}$}  => 200,  # /uploads/product/cover/42/s_9781467775687.jpg
    %r{/uploads/.*/xl_[0-9]+\.[a-z]{3}$} => 750,  # /uploads/product/cover/42/xl_9781467775687.jpg
    %r{.*} => 1024,                               # /uploads/random/spanish_inquisition.png
  }
end

「外部のplaceholderサービスを使うとリファラとかが飛んでしまうので、扱っているサイトがとても機密な場合URLそのものに機密な内容が含まれる場合には注意が必要ですね(readable slugとか使ってて /articles/増資しちゃうぞ★みたいなURLが事前におもらしすると事故)」「あー🤭、developer環境に限るのがよさそうですね」「dev環境でもプロジェクト名自体が機密だったり機能名が機密な可能性もありますので」「あ、そうか💦

「こういう外部系サービスは便利ですが、どれくらい信用してもいいのかは気をつけておかないと危ないので、今ならCSSとかSVGとかでライブラリでローカル生成するほうが気持ちとしては安心できる気がする🧐

⚓Railsのささやかな高速メソッド(RubyFlowより)

短くて読みやすい記事です。


つっつきボイス:「#sumEnumerableよりもActiveRecord::Calculationsの方が速いと」「後者はSQLでやるからそりゃ速い: RDBMSがよっぽど貧弱でない限り😆」「そうなんですね!」「これはプルリクでもよく指摘が出るヤツ: RubyのEnumerableでやるとオブジェクトがぞろぞろ出てきて1個1個足してみたいになっちゃうので、こういう処理は手元でえっちらおっちらやるよりRDBMSにお任せしましょ❤

参考: sum — ActiveRecord::Calculations
参考: instance method Enumerable#sum (Ruby 2.5.0)

「次の#max#minも同じ考えなんでしょうね」「そうそう: これもSQLでやる方が断然速い🚅」「記事でも雑に言って1000倍は違うと出てますね」

#selectより#whereを使えというのもやっぱり?」「#selectはRubyのメソッドですね: おっしゃる通り👍!」「ということは…」「RubyのEnumerable#selectはいったん全部のデータをガバっと取ってきて1個1個チェックする: そして#whereはSQLのWHEREだから取った時点で絞り込まれてるしインデックスも効くし❤」「いいことしかない😊

参考: instance method Enumerable#find_all (Ruby 2.5.0)selectのエイリアス

「だんだんわかってきた: 次の#mapより#pluck使えというのもそういうことですね😃」「そうそう: 配列が欲しいなら、ガサっと取ってから処理するEnumerable#mapよりも配列を一発で取れる#pluckでしょう: pluck :idの方がmap &:idよりもシンプルだし、SQLも的確だし」

# 同記事より
Order.where(number: 'R545612547').pluck :id
# SQL (0.8ms)  SELECT `orders`.`id` FROM `orders` WHERE `orders`.`number` = 'R545612547' ORDER BY orders.created_at DESC
=> [1]

Rails: pluckでメモリを大幅に節約する(翻訳)

「N+1クエリは、お馴染みのつらいヤツ」

Rails: パーシャルと`collection:`でN+1クエリを回避してビューを高速化(翻訳)

「今回取り上げた記事のメソッドはレビューで必ずチェックが入るヤツなので、Railsに慣れてない人はぜひ押さえておきたいですね」「データをEnumerable系メソッドで取って処理するのは非効率ということですね😃」「Railsに慣れている人にとってはもう当たり前のことばかりですが、慣れてない人がコピペでうぇ~いって書いているとやりがち」「どちらも結果は似ていますしね」

morimorihogeさんが普段から『データベースをちゃんと理解して書くべき』って言ってるのはそういうことですね: RailsはSQLログとかちゃんと見てなくても書けてしまうんですが、SQLのパワーを殺さないためにはそこが重要😎

⚓asset_sync: RailsのアセットをS3などと同期(Ruby Weeklyより)

S3やGCPやAzureで使えるそうです。

# 同リポジトリより
#config/environments/production.rb
  config.action_controller.asset_host = "//#{ENV['FOG_DIRECTORY']}.s3.amazonaws.com"

#config/environments/production.rb
config.action_controller.asset_host = "//#{ENV['FOG_DIRECTORY']}.storage.googleapis.com"

#config/environments/production.rb
config.action_controller.asset_host = "//#{ENV['AZURE_STORAGE_ACCOUNT_NAME']}.blob.core.windows.net/#{ENV['FOG_DIRECTORY']}"

つっつきボイス:「これってあるとうれしい機能でしょうか?」「これは、事前にassets:precompileした結果をS3に置いてそれを参照することで、デプロイ時にcompileしないでよくなるっていうgemだったと思います」「おーなるほど!🙇」「だからproductionが対象ということでしょうね」

「Sprocketが前提かなと思ったら、Webpackerも対応しているみたい」

# 同リポジトリより
AssetSync.configure do |config|
  # Disable automatic run on precompile in order to attach to webpacker rake task
  config.run_on_precompile = false
  # The block should return an array of file paths
  config.add_local_file_paths do
    # Support webpacker assets
    public_root = Rails.root.join("public")
    Dir.chdir(public_root) do
      packs_dir = Webpacker.config.public_output_path.relative_path_from(public_root)
      Dir[File.join(packs_dir, '/**/**')]
    end
  end
end

⚓WebブラウザのLocal Storage使うのヤメロ


同記事より


つっつきボイス:「刺激的なタイトル」「おぉ?」「cookieの方がマシと言ってるのかなと思ったら、Local Storageがcookieより容量が大きいからと油断して使うと危ないという趣旨みたい」「怒ったり泣いたり画像が忙しい😆: cookieにも大事なものを保存したらアカンけど↓」

Webアプリのセッション管理とデータ保存を学ぶ#3(社内勉強会)

「そもそもLocal Storageはpure JavaScriptで、cookieの4KBよりは多いといってもせいぜい5MBだし、文字列しか保存できないし、非同期で動かないうえにWeb Worker↓から使えないからパフォーマンス上不利だし、何よりアクセス制限がないのでJSからスイスイ見えちゃうし、だそうです」「少なくとも重要なデータを保存するのはマズそう」

参考: Web Workers API - Web API | MDN

なお、Local Storageはoriginの異なるJavaScriptからはアクセスできないとbabaさんから教わりました🙇

参考: 同一オリジンポリシー - ウェブセキュリティ | MDN

「Cookieについても、使っているWebフレームワークでSecure; HttpOnlyを有効にしておくべきとも書いてる」

参考: Secure 及び HttpOnly Cookie — HTTP Cookie - HTTP | MDN

「Local StorageにJSON Web Token(JWT)↓入れんなよ、とも書いてますね」「あぁ入れたい気持ちはとってもよくワカル☺」「認証情報はJWTでLocal Storageに保存せよと初心者に吹き込んでるブートキャンプを天国に送りたくて仕方がないようです😇: 太字で『それ嘘だから』って」「🤣」「たぶんですけど、Local Storageは開発中はとっても楽なんじゃないですかね: そして運用に乗せたときにお漏らしして詰むというパターンが想像できる」


jwt.ioより

「記事を眺めた感じでは、将来はともかく現状のLocal Storageはせいぜいキャッシュ的な位置づけにとどめておく方がいいのかなと: その意味ではcookieの方が枯れてるし(キャッシュにはならないけどなっ)」「5MBだとたかが知れてますしね: 画像が置けるわけでもないし」

「それにキャッシュがからむとコードが複雑になるし: キャッシュになかったら取りに行かなきゃならんとか、高速化のための分岐がいっぱい入るとか、本編はキャッシュなんか存在しないかのように書かないといけないとか、しかもローカルなんでユーザー側のエラーを再現できないとか、ね😢

Redisぐらい凄いキャッシュだったらよかったのに」「そうそう: 5GBぐらいあればまだキャッシュとして使いようがあったかもだけど、5MBという半端なサイズだとカジュアルにJWTとか放り込みたくなっちゃいそう」「その誘いに乗ったら負けですね👾」「そもそも自分はこの手のものは割と信用してないし😆: いつ廃れてもしょうがないぐらいの気持ちで」

「この記事翻訳したいです」「うん、これはなかなかいい記事😋

⚓その他Rails

⚓Ruby

⚓_ko1さんによるGuild情報


つっつきボイス:「久々のGuild情報です: ボリュームたっぷりでガッツリ説明されてる」「今追うには時間がないヤツ😅」「transient heapは前につっついた気がします」「この辺は逆に信じることにシテル: 中は見ないっ😆

ありました↓。

⚓speedscope: Rubyなどの言語のタイムラインをブラウザでflamegraph表示(Ruby Weeklyより)


speedscope.appより


つっつきボイス:「動かす暇がまだありませんが、RubyやJavaなどのコードをこのサイトにドロップすると、いわゆるフレームグラフがブラウザで表示できるみたいです(リンク先の「click here」で見られます)」「ほほぉ👁すっげー!」「ちょっとクールだなと思って❄」「RubyやGoやJavaScriptを調べられると」「ネイティブコードもあるし」「普段づかいとまでいかなくても、ガチでパフォーマンスチューニングをやろうとするとこういうところまで追わないといけないんでしょうね」


同リポジトリより

⚓その他Ruby


つっつきボイス:「Ruby 2.6でいよいよyield_self改めthen↓がやってくるんですね」「そりゃもう短い方がチェインするにもいいっ☺」「これまでのthenは?」「そっちはキーワードだからさすがに消えないでしょう」

# 同記事より
Event.upcoming
  .then { |events| params[:limit] ? events.limit(params[:limit]) : events }
  .then { |events| params[:status] ? events.where(status: status) : events }
# or
Event.upcoming
  .then(&method(:with_limit))
  .then(&method(:with_status))

Ruby 2.5の`yield_self`が想像以上に何だかスゴい件について(翻訳)



つっつきボイス:「次回のRubyKaigiのホテル、マジで気をつけた方がいいです: 上のリンク先で指摘されてるように、この時期(来年4月)の博多はプロ野球とか外タレ来日が重なるのでホテルが早々に埋まってしまいそう」「くわっ😳」「博多って東京の次ぐらいに外タレコンサートが多いので」「今度はもうちょっとひなびたところで😆: それはそれでホテルが足りないか」「博多は意外に街がコンパクトで、規模感はちょっと広島と似ているかも」


データサイエンスでのRubyを追求するこちらのイベント↓、終わってから気づきました😢。既にスライドがいくつか公開されています。RubyKaigiでもお馴染みのmrknさんも発表していました。

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

⚓AWSが独自のOpenJDKを発表


つっつきボイス:「AWSつながりだけなので、ちょっと置き場所が違うかなと思いつつ」「OracleはJava 11で金取るって言ってるし、きっと難民が大量発生するだろうから、そこにチャンスと食い込むのはワカル☺」「Amazon社内で密かにJDKを育成してたんでしょうね」

「Javaは仕様がオープンだしJavaを名乗るための要件も厳しい方なんで、それを満たすならOracle Javaである必要性はないし」「つまりJavaの場合オープンなのは仕様で、ライブラリの方にライセンス料が求められているんですね?」「ですです: 商用で使うなら金取るぞと」「主にGoogleから取るんでしょうね😆: GoogleこそこういうOpenJDKを出しそうな気もしますが」「最近のGoogleはJavaキライなんじゃないかな?😆」「そうだったかも: 割と前にGoogleがJavaやめてC#に移ろうかなって言ってるという記事を見た覚えがあるんですが、うまく見つからない…」

今のGoogleは少なくともAndroidではKotlin推しですね。

参考: Google、KotlinをAndroidアプリ開発言語に選定――I/O会場から大喝采 | TechCrunch Japan

⚓その他クラウド


つっつきボイス:「セキュリティ系のブログで、Linuxサーバー用のAntiVirusソフトウェアのリストが紹介されています」「そういえば大昔にMacがまだマイナーだった頃はセキュリティソフトなんかなくったっておk、なんて言われてたし😆」「そうそう😆」「もうマイナーとはとても言えない😆

⚓SQL

⚓Basecampがintカラム問題でダウン(DB Weeklyより)


つっつきボイス:「RailsでしかもDHHのブログですが内容的にここに置いてみました」「データベースのintカラムのせいでBasecampがしばらく落ちたのか😳: 上手の手から水が漏るというか🚰」「Railsの例のbigint問題」「この間の公開つっつきでも話題に出た、以前のRailsだとデータベーステーブルのidカラムがデフォルトではbigintでなかったというヤツですね」「DHH記事にもありますが、bigintなら9223372036854775807までいけるのに、普通のintカラムだと2147483647を超えたらあふれちゃう」「運用してれば割とあっさりこの値を超えちゃいますからね😇

参考: Rails5.1からidカラムがbigintになるのでその対応 - koukiblog

「ブログの最終パラグラフがハイライトされまくってます↓」「DHHが全面的に頭を下げてる」「えらいすまんかったと」「こればっかりはね〜、事前に読み切れないことはどうしてもあるので」「経過をきっちり記載しつつ、非は自分にあると一切言い訳せずにきっぱりお詫びしているのが凄いですね(自分にできるだろうか💦)」

このブログハイライト機能、TechRachoにも欲しいです。

「We should have …」は学校英語でも学ぶ仮定法過去完了という反実仮想の用法ですが、いわゆる商用サイトの遠回しなお詫び文とは違う、かなりストレートな後悔の心情を表現していると思います。

話は逸れますが、主語がyouの「You could have …」という仮定法過去完了は「どうしてあのとき…しなかったの!」と相手を非難するときとか「…しとけばよかったのに(バカダネー)」とこき下ろすときにも使われます。

⚓その他SQL


同記事より


つっつきボイス:「Google Spreadsheetの関数にSQL「風」のクエリを書けるんだそうです: Excelにも似たようなのがあった気がしますが」「クエリ書けるんだ、すげっ😳: ガチのSQLとは違いそうだけど」「開発で直接使うことはあまりなさそうですけど、納品するシートなんかにはいいかなと思って」

「この辺は妄想ベースですが、こういうふうにシートでクエリを書けるなら、シートをデータベースサーバーにできたらある意味面白いかも?」「😆」「セキュリティとかその辺はさておいて😆、永続化層のひとつとして選択肢に含められるかも」「これで十分な場合もありそう」「シートをこうやってクエリで扱ってJSONを吐いたりできるなら意外と親和性高いかも?」「シートをきちんとロックするとかスナップショットを取ってデータがぶれないようにすればいけそう」「SQLが使えるなら原理的にActive Recordも使えるし(JOINとかはちとコワいけど😅)」

「管理画面を作るほどでもない、みたいなときなんかによさそうですね」「あまり更新されないマスターテーブルとかで、扱うのがエンジニア1人だけで、もう少し複雑になるまでは管理画面なしで運用したいとかね😋: やったことないのでできるかどうかわからないけど😆

⚓JavaScript

⚓js-primer: ECMAScript 2018時代のJavaScript入門書

先日公開した「Railsウォッチサマリー」↓を編集していてやっとTypeScript入門以前ガイド - mizchi’s blogを読み始めたところ、冒頭でこのjs-primerが推薦されていました。

Railsウォッチサマリー: 2018/10(社内勉強会)


つっつきボイス:「このjs-primerというサイト、まだWIPで、私も読み中なんですが、じーんとくるほどいい内容でした😂: JavaScriptの文法や機能を歴史も含めてひとつひとつ説明しつつ、その場その場で新しいベストプラクティスや注意事項も書いてくれている」「ほーこれはいいですね☺: JavaScriptにご無沙汰してた人とかによさそう」「私みたいにJavaScriptをマダラに覚えてしまった人には本当にありがたいです🙏」「まったくのJS初心者にはちょっとわかりにくいかも」

「まあバックエンドエンジニアだとJavaScriptについて後ろ手に回りがちですしね☺」「タイトルを思い出せないんですが、以前オライリーでいいとされていたJavaScript本を買ったら『それもう古いですヨ』って言われたことがありました😢

「このstrict modeも早速自分のJSコードで書いてみました↓」「いきなり書くと動かなくなりますヨ😆」「頑張ってletconstで書いてあったおかげかすっと通りました😋」(以下延々)

"use strict";
// このコードはstrict modeで実行される

「文章の質もとてもいいと思います😃: こうやって明快に書ける人はスゴイ!」「いつか完成するといいなと思います」

その後ようやっと第一部を読み終わりました🙇

⚓その他JavaScript


つっつきボイス:「このfx、CLIなのにクリックでJSONを展開できます」「どうやってるんだ?これもスゲー😳

リポジトリのアニメーションgifが重いのでリンク先でどうぞ🙇

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

⚓HTTP-over-QUICが「HTTP/3」になるか


つっつきボイス:「この間HTTP/2が出たと思ったらもう3ですか」「😆」「まだ心はHTTP 1.1😆

参考: HTTP/3が出るらしいという話を雑に書く - Qiita

⚓Bootstrapに特化したPhotoshopエクステンション


同サイトより


つっつきボイス:「BPSのデザインチームから喜びの声が上がっていました: Bootstrapに特化したPhotoshoのエクステンションだそうです」「おぉ?」「上のツールだけ使っていればBootstrapコンパチになるようです」「そりゃスゲー!」「動画をみるとグリッドも出てパーツが気持ちよく並んでくれてますね」

「ところでこれってHTMLになってくれる?」「結局PSDなのかな😆」「ま、PSDでもカラムとかきちっと揃ってくれるだけでありがたい☺

「これがiPadアプリになったら、たとえば打ち合わせの場でBootstrapコンシャスなカンプを見せてお客さんにその場で調整してもらうなんてこともできそうですよね」「それあってもいいかも😊

⚓言語

⚓プログラミング言語の欠点とは


つっつきボイス:「常々自分に足りないのがこのデバッグ力だと思ってます😅: デバッグ力って、言ってみればシャーロック・ホームズ的に限られた材料から推理する力だなーと」「推理する力、わかるー: 自分もたとえばマルチスレッドのデバッグなんかで、直接原因を突き止められなくてもそこに原因があると論理的に追い込んだりするのが大好き❤

⚓分子構造をマークアップ的に記述


ja.wikipedia.orgより


つっつきボイス:「SMILESという、分子構造記述用のマークアップ的言語があるそうです」「ほーこんなものがあるとは😅: マークアップを食わせれば分子の絵を生成してくれたりするんでしょうね」「タンパク質なんかだと分子が数万とか数十万とかものすごくデカくて組み合わせも爆発しまくるので、正規表現ですら解析に四苦八苦するみたいです」「ボクたちはこうやって見てるだけだからいいけど研究者は大変そう🤯」「ちなみに上のWikipediaからたどったサンプルサイトは古いのが多いみたいで何だかうまく動きませんでした😅

⚓その他

⚓機械学習に役立つ数学

スライド最終ページの結論がステキです❤

⚓その他のその他



同サイトより


つっつきボイス:「上の機械学習スライドの人のツイートなんですが、数理空間トポスって何だろうと思ったら、現代数学に興味を持つ中高生を現代数学ガチ勢のチューターが寄ってたかってサポートする場だそうです: ウラヤマシイー😭」「トポスって何だっけとググってみると…場所!」「トポスっていかにもギリシャ語ですね☺: ロゴスとかパトスとかエートスとか」「ついでに数学のトポスはというと…『位相空間上の層のなす圏を一般化した概念』なるほどワカラン😆」「一般化を複数回やってる…?😱


同サイトより

「こういう集まりはもっとやるべきと思うっ😤」「思います😊

⚓番外

⚓太陽熱を貯蔵する液体

参考: 太陽熱を最長18年貯蔵できる、画期的な太陽熱燃料が開発される | ワールド | 最新記事 | ニューズウィーク日本版 オフィシャルサイト

⚓GDPトップ争い


つっつきボイス:「リンク先の動画を埋め込めなかったのでクリックでどぞ」「おお、おお、面白いー!😀」「日本のGDPが伸びたと思ったらバブルでしぼんで、中国が後からガンガン伸びてくると」「米国、常にトップか😆」「米国を基準にしてるみたい」「競馬の楽しさ」「こうしてみると日本って停滞してるんだなーと」(以下延々)


今回は以上です。

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

週刊Railsウォッチ(20181112)Ruby 2.6.0-preview3リリース、非同期スレッドのテストはつらい、MySQL 8のGROUP BYほか

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

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

Rails公式ニュース

Ruby Weekly

RubyFlow

160928_1638_XvIP4h

Hacklines

Hacklines

DB Weekly

db_weekly_banner


DELL XPS 15 9550を3年近く使った振り返り

$
0
0

サブマシンとして愛用していたXPS 15 9550がもうすぐ3歳なので、振り返りをしてみようと思います。

自分のノートPCとの付き合い方

10年以上前から、幾度となくハイスペックなノートPCをメインにしようと試みたことはありますが、自分の場合は定着しませんでした。@morimorihogeさんのようにメイン環境を持ち運んでどこでも作業できるのに憧れはするのですが、

  • たまにChromiumビルドするような仕事があるので、マシンパワーは妥協したくない。
  • ウィンドウの位置をなんとなく配置するのに慣れすぎて、外部ディスプレイ抜いたら移動してしまうのにどうしても耐えられない。
  • メインPCとサブPCがあることで、テストに役立つので、結局常時2台を手元に置く必要がある。
  • 会社で座って作業するのが一番集中できる人間なので、持ち運べる意味があまりない。

というあたりで、メインのデスクトップ + サブのノートPCというのが定着しています。

ノートPCに求めること

サブマシンなので、主な用途は移動中やミーティングなど自席以外での作業(資料を見たり書いたりSSHで緊急対応したり)、およびメインのデスクトップPCでは難しいタイプの開発・テスト・デバッグです。

  • わくわく感があること
    • 気に入るかどうかはモチベーションに直結するのでとても大切です。どんなに性能が良くても、見た目が気に入らなかったり中華しすぎているとわくわく感が削がれるのでNGです。
  • タッチパネルを搭載していること
    • タッチ操作対応の開発をするときに最近は必須です。
  • 高解像度ディスプレイを搭載していること
    • システムDPI設定を100%より大きくしたときだけ発生するバグは多いので、重要です。
  • 毎日持ち運んで使う気になる重さとバッテリー持ち
    • 重さ2kgくらいまで、バッテリーは省エネしながら4-5時間くらい持てば許容範囲。

VAIOかSurfaceと悩んだ結果、当時は値段とのバランス(と2連続でVAIOだったので気分転嫁)でXPS 15にしました。

良かった点

1. とても硬い

アルミ削り出しのボディは、とても硬くてたわみが少ないです。まあ外観と外側の質感は薄くなる前のMacBook Proそのまんまですが、頑丈そうなのは気分が良いです。

(硬いから壊れないというわけではなく、何度か開いたまま隅っこを持って負荷をかけたせいかヒンジのあたりに違和感が出始めていますが、とはいえふにゃふにゃよりは良いと思います)

また、後述の通りキーボードは頑丈ではないですが、カーボンファイバーのパームレストはわずかに染みができる程度でほとんど劣化の気配がないのは立派だと思います。

2. ポート類が必要最低限そろっている

フルサイズUSBが2個とHDMIがあるので、アダプタ類を持ち歩かなくても会議室のテレビやスピーカーマイクに接続できるのはとても便利です。
主にこれが原因で、XPS 15 2-in-1や最近の小型PCへの買い換えに躊躇しています。

3. MacBook ProユーザーからACアダプタを借りられる

MacBook Pro 15インチ付属の87WアダプタからUSB Cケーブルをつないだら、ゆっくりと充電できました。いざというときに頼れるのは良いものです(45Wなどで充電できるかは未確認)。

気になった点

1. スリープから勝手に復帰していることがある

どのPCでもあることですが、スリープしたまま鞄に入れておいたら勝手に復帰して熱くなっていることがたまにありました。お弁当と一緒に入れると傷むので気をつけましょう。

しばらく使ってわかったことは、BIOSやドライバのアップデート直後に発生しやすく、その次のアップデートをしたり1週間くらいすると自然に落ち着くということです。発生時期が予想できるようになってからは、心の準備ができるのであまり気にならなくなりました。

2. スリープ復帰が少し遅い

スリープ復帰が、3秒くらいですっといくこともあれば、何やら30秒くらいかかったり、ログイン成功したあとしばらくマウスカーソルがウニョウニョ動いてキー入力を受け付けない状態になることがたまによくあります。1~2年使ってからだんだん発生頻度が上がってきた気がするので、多分トラックパッドのあたりで何かノイズ拾ってるのか、ディスクIOが詰まってプチフリして挙動不審になってるのかなどと思っていますが、よくわかりません。

3. タッチパッドの慣性スクロールの止め方がわからない

タッチパッドはWindows 10の「高精度タッチパッド」に対応しており、Windows PCの中では使いやすい部類に入ると思います。

ただ、2本指でスクロールできるのは良いのですが、スクロールすると必ず慣性がついて、これが勢いによりますが3秒ほど流れます。「ブラウザで大きくスクロール → 慣性が終わる前にCtrl+Tabでタブ切り替え」すると、Ctrlを押した時点でCtrl+ホイールの拡大が働き、ページが極端に拡大されることがよくあるのがとてもストレスです。このためだけに外部マウスが欲しい。

4. キーボードが割れた

2年ほど使っていたら、キーボードの↑キーが割れてしまいました。

ヤフオクでキーボードを3000円弱で購入し、パチッと取り替えたら良くなりました。まるごと取り替えるのは面倒だったのでキートップだけ差し替えています。

5. バッテリーが膨らんだ

2年少し使っていたら、タッチパッドがクリックしづらいことに気づきました。ポンッとタッチするタイプのクリック(2本指では右クリック)はできるので、不自由はないのですが、カチッと押し込むのができません。

よく見るとタッチパッドがパームレストから出っ張っています。こういうものだったのかな?と思って数ヶ月放置していたのですが、新品を見たらタッチパッドはパームレストから凹んでいたので、どうやらおかしいと気づきました。

原因は、タッチパッドの真下にあるバッテリーの膨張です。ということで、バッテリーを交換しました。ヤフオクで9000円くらいで新品バッテリーを購入しました。シールを信じるなら純正品です。

分解には、小さめのプラスドライバーの他に、裏蓋を開けるのにT5サイズのトルクスドライバーが必要です。Amazonで安く売っていたこれでバッチリでした。一家に1セット常備しておきたいですね。

分解してみたら、膨らんでいました。

バッテリーは裏蓋さえ開ければ一番簡単に外せる場所にあります。サクッと外します。

新旧のバッテリー比較。

バッテリーを交換したら、タッチパッドが無事に収まり、クリックもしやすくなりました。めでたしめでたし。

6. サポートの品質は良くない

購入直後(2016年初)に追加ACアダプタを購入したとき、2018年春に、キーボードが壊れて純正保守部品を購入しようとしたとき、の2回サポートに連絡しましたが、対応品質はお世辞にも良いとはいえませんでした。

  • 当時公式ショップに純正ACアダプタが記載されておらず、チャットで問い合わせると「取り扱っていない」と言われた。
    • 翌日電話すると別の部署につながり注文できた。
  • キーボードが壊れたので部品を注文しようとして、値段を聞いたら「価格表はない、見積書の発行に1週間かかる」と言われた。
    • 注文するにはPDFを返送する必要があるが、フォームになっていないので、印刷して記入してスキャンするか、Photoshopなどで加工する必要がある。
    • トータル2週間以上たって届いたものは全然違う部品で、結局正しい部品は値段が3倍だったので、返品した。返信をもらうのに10日以上、返金されるのに1ヶ月ほどかかった。

有償サポートを契約すれば違うのかもしれませんが、別にサポートが欲しいわけではなく部品を普通に注文したいだけ(初期不良以外でパーツ交換で直らないレベルで壊れたら諦めるつもり)なので、ヤフオクを使う方が精神衛生上良いという結論に至りました。

3歳の誕生日を前に

いくつか気になることはありますが、僕にしては長く3年近く使っている程度に、トータルでは気に入っています。4Kタッチパネルは開発・テストに使いやすく、購入当初はいらないと思っていたdGPUも、GPU依存の不具合を洗い出すのに以外と便利です。またThinkPadほどではないですが、修理部品がヤフオクで容易に入手できるのもDELLの強みだと思います。

とはいえそろそろ次の機種を探す時期です。XPS 13 2-in-1は気になっていますがUSB Cしかないのと、同じメーカー連続だとわくわく感が削がれるので、VAIOが息を吹き返さなければSurfaceかHPかなあ…

SendGridで独自ドメインからdocomo宛に送信するときの注意点

$
0
0

SendGridで独自ドメインから送信する際は、Sender Authentication機能を利用してSPFDKIMの設定をすることが一般的です。

これで基本的にはOKなのですが、この方法だとSPFは通過するもののSender IDやdocomo独自の検証は通過しないため、docomoのキャリアメールに対して届きにくいことがあります。

FromとEnvelope From

まず、メールのFromとEnvelope Fromについておさらいしましょう。

From

Fromアドレス(ヘッダFrom)は、メールヘッダに記載されるアドレスで、一般のメーラーで「送信者」として表示されるものです。

例えばこのAmazonのメールでは、 auto-confirm@amazon.co.jp がFromアドレスになります。

Envelope From

Envelope FromはSMTPの MAIL FROM で指定するもので、バウンスメールの宛先として利用されます。送信時にヘッダには記載しませんが、最後のMTAが Return-Path としてヘッダに追記するため、受信側はそれを見ることでEnvelope Fromを確認できます。

先ほどのAmazonのメールでは、 Return-Path: <ユニークなIDらしき英数字列@bounces.amazon.co.jp> になっていました。

telnetで送信する際の例

telnetでSMTPを送信する以下のようなシンプルな例で見てみましょう。赤字が自分で入力した文字列です。

baba@ubuntu:~$ telnet localhost 25
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
220 ubuntu ESMTP Postfix (Ubuntu)
EHLO localhost
250-ubuntu
250-PIPELINING
250-SIZE 10240000
250-VRFY
250-ETRN
250-STARTTLS
250-ENHANCEDSTATUSCODES
250-8BITMIME
250 DSN
MAIL FROM: mail0001@bounces.example.com
250 2.1.0 Ok
RCPT TO: baba@bpsinc.example
250 2.1.5 Ok
DATA
354 End data with .
From: noreply@example.com
Subject: Hello, world

.
250 2.0.0 Ok: queued as 7C508
quit

この例では、以下のようになります。

  • mail0001@bounces.example.com がEnvelope Fromです。ここにユニークなアドレスを指定することで、バウンスを検知しやすくなります。
  • noreply@example.com がFromです。これは受信者に表示され、また Reply-To ヘッダを追加しない限り返信先にもなるので、メールの目的に応じて適切なものを設定します。
  • baba@bpsinc.example がEnvelope Toです。これが実際に送信される宛先で、このアドレスとヘッダのToを別のものにすることで、BCCを実現できます。
  • ヘッダにToアドレスは指定していませんので、受信者から見ると「宛先無し」のメールに見えます。

SPFとSender ID

SPFとSender IDはどちらも、ドメインに記載したTXTレコードを使って送信元アドレスを認証する技術です。
例えば bpsinc.example ドメインに以下のようなDNSレコードが設定されていた場合、送信元アドレスが mail.bpsinc.example のメールは 198.51.100.1 のメールサーバから送るよ(それ以外は偽物だよ)と言うことになります。

txt mail v=spf1 +ip4:198.51.100.1

SPFとSender IDはどちらも上記のTXTレコードを使いますが、「送信元アドレス」としてどれを見るかが異なります。

SPFの場合

SPFではEnvelope Fromをチェックします。

Sender IDの場合

Sender IDではPurported Responsible Address (PRA) をチェックします。
詳細は RFC4407 に記載がありますが、通常はヘッダのFromアドレスが使われることが多いと思います。

SPFと違い、ユーザに見える送信者アドレスをチェックすることになるので、なりすまし対策としてはより踏み込んだものになります。

Gmailでの確認

Gmailでは「Show Original」からメッセージヘッダを表示する画面で、SPF(やDKIM、DMARC)の通過状況を確認することができます。

現在のところ、Sender IDには対応していないようです。

SendGridでのSPF対応

前置きが長くなりましたが、SendGridでの設定について見てみましょう。

ダッシュボードからSender Authenticationを開き進んでいくと、以下のようなCNAMEを設定するように指定されます(一部数字を0に伏せてあります)。

cname sendgrid u0000000.w0000.sendgrid.net.
cname s1._domainkey s1.domainkey.u0000000.w0000.sendgrid.net.
cname s2._domainkey s2.domainkey.u0000000.w0000.sendgrid.net.

DKIMはいったん置いておくとして、SPF用のレコードは cname sendgrid u0000000.w0000.sendgrid.net. です。
SPFはTXTレコードで v=spf1 +ip4:198.51.100.1 のような形で指定するものですが、これだとSendGrid側でサーバのIPアドレスを変えた際に全利用者が変更しなくてはならなくなるため、このような設定が推奨されているようです。

上記の設定を example.com に行った場合、SendGridから送信するときにはEnvelope Fromに @sendgrid.example.com が利用されるため、SPFが無事に通過します。

問題点

上記の設定方法からわかるとおり、SendGridからの送信では、必ずEnvelope Fromに何らかのサブドメインが付与され、SPFもそのサブドメインに設定します。
そのため、Fromアドレスを sender@example.com のようにサブドメインを付けないアドレスにして、そのアドレスがPRAになる場合、Sender IDを通過しないことになります。

多くのメールサーバやau, softbankのキャリアメールはSPFが通過すれば問題ないのですが、docomoではSender ID(もどき?)が使われているため、このままだとなりすましメール扱いされるリスクが高まります。

SPF(TXT)レコードの確認には、メールヘッダのFROMフィールドを使用します。なお、メールヘッダのFROMフィールドが存在しない場合はエンベロープFROMを使用します。
同記事より

対策

Fromアドレスを @sendgrid.example.com のようにするのは一つの手ですが、あまり格好良くありません。

Sender IDやdocomoに対応するには、以下のようなレコードを設定するのがお手軽だと思います。

txt @ v=spf1 include:sendgrid.example.com

これで、サブドメインのつかないメールアドレスでも sendgrid.example.com の中身を見ることになるため、自前でSendGridサーバのIPアドレス変更を検知・処理することなく、安心して使うことができます。

関連記事

Gmailで突然メールが届かなくなった場合の対応方法

週刊Railsウォッチ(20190225)Rails 6のあまり知られてない新機能、TWAとKotlin/NativeとFlutter、Rubyが26歳の誕生日ほか

$
0
0

こんにちは、hachi8833です。スクショ禁止の分科会決定でひっそりざわついてます。


つっつきボイス:「ちーっす」「お、今日は久しぶりに冒頭から参加😍」「いや〜もう疲れちゃったんで今日はこれで仕事終わりにしようと思って😅」「そういうときはウォッチつっつきで気分転換しましょ🏖」「そのつもりです☺」「そうそう、疲れたときはRailsウォッチ😆」「今日納品とかじゃなければ全然OK🚛: ちょうど長い実装やっと終わったんですよね」(以下延々)

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

⚓週刊Railsウォッチ「公開つっつき会#8」開催のお知らせ

次回は3/7(木)開催です。皆さまのお気軽なご参加をお待ちしております🙇

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

コミットリストから見繕いました。@tenderloveさんのテンプレート改修が目立ちます。

⚓PostgreSQLのUUIDキャストのアロケーションを削減

# activerecord/lib/active_record/connection_adapters/postgresql/oid/uuid.rb#L7
        class Uuid < Type::Value # :nodoc:
          ACCEPTABLE_UUID = %r{\A(\{)?([a-fA-F0-9]{4}-?){8}(?(1)\}|)\z}
          alias_method :serialize, :deserialize
          def type
            :uuid
          end

-         def cast(value)
-           value.to_s[ACCEPTABLE_UUID, 0]
-         end
+         private
+
+           def cast_value(value)
+             casted = value.to_s
+             casted if casted.match?(ACCEPTABLE_UUID)
+           end
        end

つっつきボイス:「おぉ〜UUIDをプライマリキーに?」「UUIDをstringで取れるようになったのか」

「どうなんだろうね〜、UUIDをプライマリキーにしようみたいな話はIDが溢れた経験のある人はみんな言ってますけど😆」「😆」「とはいえRailsのURLにあの長ったらしいUUIDが出てくるのもちょっとね〜」「マイクロソフトの7a98c250-6808-11cf-b73b-00aa00b677a7みたいなUUIDとさんざんお付き合いしたことあります😅

参考: Generating the UUID - Windows applications | Microsoft Docs

⚓ActiveSupportのZeitwerkIntegrationunhook!を追加

# activesupport/lib/active_support/dependencies/zeitwerk_integration.rb#L30

        def verbose=(verbose)
          l = verbose ? (logger || Rails.logger).method(:debug) : nil
          Rails.autoloaders.each { |autoloader| autoloader.logger = l }
        end

+        def unhook!
+         :no_op
+       end

zeitwerkが1.2にアップグレードされたのも07346で取り込まれていました。


つっつきボイス:「zeitwerk、発音なんだっけ」「えっと、ツァイトヴェルクだったかな」「コミットしてるfxnさんがzeitwerkやってる人ですね」

「Zeitwerkのコミット数や規模が思ったより小さくてちょっとびっくりしました」「オートローダーやってるだけだから小さいんでしょうね: Rubyの新しい機能を使っていい感じのオートローダーを作った的な」

⚓レンダラーがstringではなくレンダリング済みテンプレートオブジェクトを返すよう変更

# actionview/lib/action_view/base.rb#L289
-   def in_context(options, locals)
+   def in_rendering_context(options)
      old_view_renderer  = @view_renderer
      old_lookup_context = @lookup_context

      if !lookup_context.html_fallback_for_js && options[:formats]
        formats = Array(options[:formats])
        if formats == [:js]
          formats << :html
        end
        @lookup_context = lookup_context.with_prepended_formats(formats)
        @view_renderer = ActionView::Renderer.new @lookup_context
      end
      yield @view_renderer
    ensure
      @view_renderer = old_view_renderer
      @lookup_context = old_lookup_context
    end
    ActiveSupport.run_load_hooks(:action_view, self)
  end
end

つっつきボイス:「いきなりstringを返すんじゃなくてテンプレートのまま返すようにしたのね」「最近のtenderloveさんはずっとテンプレートレンダラー周りにかかりっきりですね」「Railsの表示を速くしたいからだったりして😆: 主にGitHubの」「tenderloveさんはGitHubの人ですしね」

「実際レンダリング周りって、Rails的に書き進めていくと極限に遅くなるし: 表示完了まで15秒かかったなんてこともあったし🐌」「へ〜」「要するにパーシャルに分ければ分けるほど、どんどん遅くなる」「あ〜そんな話もありますね」「パーシャル化を進めていくと、テーブルのところとかでパーシャルが入れ子になってしまったりするんですが、そういうところで横のループと縦のループが形成されると遅くなって死ねるという🐢」「あんまり遅くなるから今度はパーシャルを結合していったりとか😆」「😆」「Railsでパーシャルを増やしたときの遅さはもう少し何とかしないといけない気がする🤔

⚓rendered_formatを非推奨化

# actionview/lib/action_view/rendering.rb#L25
  module Rendering
    extend ActiveSupport::Concern
    include ActionView::ViewPaths

+   attr_reader :rendered_format
+
+   def initialize
+     @rendered_format = nil
+     super
+   end
...

-   def rendered_format
-     Template::Types[lookup_context.rendered_format]
-   end

つっつきボイス:「rendered_formatを非推奨…?」「この副作用に依存しなくなったから消すと」「普段触らないところだからよくわからないけど、たま〜にこういうのが必要になったりしますね😆」「たしかに〜」「ごくまれにここのコンテキストのフォーマットを取りたいみたいなのが😆

参考: rendered_formatAbstractController::Rendering

「Railsじゃないけど、以前PHPのフレームワークでどうしてもコンテキストを取りたいことがあって、フレームワークのソースをどっさり読んでまで取ったのをちょっと思い出した😭」「わかりみ」「ああいうのはツラいホント😇

⚓delete_bydestroy_byを追加

#35304でDHHが足そうと決めたようです。

# #35304より
# Before
unreads.find_by(readable: readable)&.destroy
unreads.where(readable: readable).destroy_all
unreads.where(readable: readable).delete_all

# After
unreads.destroy_by(readable: readable)
unreads.destroy_by(readable: readable)
unreads.delete_by(readable: readable)

つっつきボイス:「あ〜これが欲しいのわかる!」「たしかに」「delete_byとかdestroy_by、あっていい😋

delete_byだとActiveRecordのオブジェクトを作らないことができそう?destroy_byだとフックが動くからActiveRecordのオブジェクトを作らないといけないだろうけど」「どうでしょう?わかんないけど、destroy_byの動作に寄せるんじゃないかな〜🤔: destroyだと削除済みのActiveRecordオブジェクトが返ってきた気がするし、find_byもオブジェクト返すから同じにするのかななんて」「persistedがオフになったオブジェクトを返すみたいな?」「たぶんそんな感じで」

destroyって、うろ覚えだけどsaveみたいな他のメソッドとちょっと動きが違ってて、たしかtrue/falseを返さずに、削除済みのオブジェクトを返してた気がする: でdestroyed?すると削除に成功したかどうかがわかるみたいな作りだったと思うんですよね」「あーそういうのどっかで見たことある」

参考: destroyed?ActiveRecord::Persistence

「だから厳密なことを言えば、削除が成功したかどうかをdestroyed?でチェックするのが筋なんですけど、普通みんなそこまでやらないっすよね😆」「destroyしたらもう次にはリダイレクトかけたりとか😆」「trueじゃないかもしれないんだぞと😆」「まあエスケープが別の例外吐くかもしれませんが😆」「いずれにしろdestroyってちょっと変なAPIだった覚えがあるんですよね」

「でも削除したオブジェクトを取れれば『〜を削除しました』みたいなメッセージを作りやすいし」「あ、確かに!」「delete_byとかdestroy_by、たしかにあっていいヤツ👍

⚓Rails

⚓Rails 6のあまり前面に出ていない新機能


つっつきボイス:「TechRacho読んでればだいたい知ってること多いかな」「そんな気がしますね」


「そうそう、Webpackerが6でデフォルトになるし」


「暗号化credentialで環境をサポート」「つかそれがないと使いものにならないと思うんですけど🤣」「🤣」「環境使い分けられないcredentialを誰が使うのかと🤣」「credential周りも何だかずっとやってる感」「gemがあるからそんなに不自由しないのかもしれないけど」


ActionDispatch::HostAuthorizationってのができたのか、へ〜」「Hostヘッダー攻撃を防ぐためのものですか」

参考: What Is a Host Header Attack? - DZone Security


translationメソッドと_html、これあったな」「あったあった(ウォッチ20180723)」「i18nでこんなふうに↓_html付けると式展開が自動でエスケープされるヤツ」

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

Relation#pick?」「あー、pluckの単発バージョンみたいな」「LIMIT 1がつくのね」


update_attributesが非推奨になる件、ちょうどこないだ社内のコードレビューでやりましたね」「ウォッチで取り上げられてたんで、そのリンク(ウォッチ20190218)をそのままレビューに貼って『非推奨になるらしいっすよ』って使いました😆」「やったね😋」「業務で役に立った😋


「MySQLのデフォルトエンコーディングがutf8mb4になる件」「まだ対応してなかったんだっけ?」「これもウォッチで扱った覚えが(ウォッチ20180925)」「たしかこの対応のためにMySQLのミニマムバージョンが引き上げられた」「そういえばそっちの方が大きい話だったかも」


rails db:system:changeができるようになった話(ウォッチ20190121😆」 「へぇ〜」「create_table: :if_not_existもあった(ウォッチ20181203)」


「セッションオブジェクトのナビゲーション?」「digというハッシュ的なメソッドでセッションオブジェクトを安全に扱えるようになったのか」


Enumerable#index_withですって(ウォッチ20180608)」「当時の記事で『これは優秀なメソッド』ってありますね😋


Array#extract!ウォッチ20180827)」「Arrayでやれるのか〜」


rails server -u pumaみたいにサーバーを指定できるようになるし」「ほぉ〜」


「サマリーとしていい記事👍: ダラダラ書いてないのも好感持てるし」「Railsウォッチを毎週読んでない人にはこういう記事もいいかも☺」「そうですね☺

⚓Rails 5.2向け厳選gem 10種(Ruby Weeklyより)

「ちょっと時間が押してるので簡単に」「SidekiqにPryにDalli」「そしてexception_notificationは定番かな」

「プッシュ通知のFCMにWicked PDF」「Chartkickはグラフ生成」「こういうのいっぱいありすぎて😭

Wicked PDFとChartkickは昔の翻訳記事にありました↓。

Ruby on Railsで使ってうれしい19のgem(翻訳)

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

「AWS-SDK S3も有名」

「おお、will_pagenateなんてなつかしい👴」「ここだけ急に古いですね」「まだ使ってる人いたんだ〜」「kaminariとwill_pagenateって同じものかと思ってた😅」「will_pagenateってこんなきれいに出ましたっけ↓😆」「さすがにBootstrapとか当ててんじゃね?😆」「最近だとPagyが速いらしいけど」


同記事より

Rails:「Pagy」gemでRailsアプリを高速ページネーション(翻訳)

「active_model_serializersも定番」「これはある意味難しいgemだけど、ちょろいAPIとかだったらこれでいけたりしそうではある」「少なくともjbuilderで全部やろうとするよりは全然いい」「おや、active_model_serializers、長いこと更新されてないっぽいな」「ちょい悩ましい」「active_model_serializersのリポジトリってrails-apiっていうんですね」「何だか公式っぽい雰囲気?」「微妙にイラッとする😆」「あ、そうじゃなくてactive_model_serializersってRailsにマージされてるのか!」「しかしこの更新のされなさはちょっとためらいを感じる😆」「完成され尽くしてるのかも😆」「いやいや😆」「モデルに対するDecoratorみたいなgemだし」「JSONにするためのgemというか」「何となれば自分たちでメンテできそうなささやかな規模感ではあるけど」「あり方はビューっぽいけど、それをとてもモデルっぽく書けるgemですよね☺

「active_model_serializers、リポジトリにソースが見当たらない…?」「他のどっかに置いてあるとか?」「もしかしてRSpecみたいな感じだったりして: RSpecって本体にはほとんど中身がなくて、実際の中身はrspec-coreとかそういうところに置かれてたりするし」「active_model_serializersのソースの場所やっぱりよくわからん😅」「RubyMineとかだったら何も考えずにジャンプできるからソース探し回らなくていいんだけどな〜」

「active_model_serializersみたいなgemはソース読まないとわからない感じですよね」「このgemを使ってきれいに書いたサンプルコードとかないと使いにくい感じありますね」「root要素を指定できるオプションなんかもあって確かに欲しいヤツなんだけどデフォルトの挙動がよくわかんないし😅」「undocumentedとまではいかないにしても情報がちと少ない感」「触ってみないとわからない感じなんですね」「このgemは入れてすぐ使えると思わないほうがいいかも😆: 1日ぐらいああでもないこうでもないとかやってみないと(実際やったし)」「自分もやりましたよ😅

⚓rubanok: params周りをすっきりさせるgem(Ruby Weeklyより)

久しぶりにEvil Martiansがスポンサーのgemです。Rails以外でも使えるそうです。

# 同リポジトリより
class CourseSessionController < ApplicationController
  def index
    @sessions = CourseSession.
                  search(params[:q]).
                  by_course_type(params[:course_type_id]).
                  by_role(params[:role_id]).
                  paginate(page_params).
                  order(ordering_params)
  end
end

# 上を下のように書ける
class CourseSessionController < ApplicationController
  def index
    @sessions = planish(
      # pass input
      CourseSession.all,
      # pass params
      params,
      # provide a plane to use
      with: CourseSessionsPlane
    )
  end
end

# 全部推測させたりも
class CourseSessionController < ApplicationController
  def index
    @sessions = planish(CourseSession.all)
  end
end

つっつきボイス:「これは嬉しいヤツでしょうか?」「ははーん、paramsを直で渡してよしなに解釈してくれる感じかな」「Ransackみたいな?」「Query Objectに近い感じ」「このgemをいきなり使われたら意味わかんなくてびっくりしそうではある😆」「上みたいにbefore/afterで示されてればわかるけど、afterだけ見せられたらヘ(゚д゚)ノ ナニコレ?みたいな😆」「😆」「こういうのを柔軟にやるのはなかなか大変そう…」「やりたいことはわかるけど、それが他の開発者に伝わるかどうかが心配だな〜: その意味でこういうやり方はちょっと😅

⚓VelocityとGitPrimeをチェックした


つっつきボイス:「Code Climateさんの記事です」「VelocityとGitPrimeって何だろうと思ったらプロダクトみたい」「VelocityはCode Climateの自社製品なんですね」


codeclimate.comより


gitprime.comより

「Velocity、ちゃんと見てないけどNewRelicとかDatadogとかみたいな感じに見える」「アプリケーションメトリクスを収集してビジュアル表示するみたいな感じ?」「Datadogが近いのかも🤔」「ということはGitPrimeはVelocityのライバルさんかしら」

「ところで、Code Climateの自動レビューみたいなエンジニアのコミュニケーションを自動化するみたいなツールって、CIとかを除けばなぜか日本であまり定着しないんですが、その理由はもしかすると日本では結局日本語でやりとりしてるからじゃないかなという気がしてます」「あ〜」「ほら、海外だとプライマリ言語が英語じゃない同士が英語でやりとりしてたりするじゃないですか: そういう日常会話レベルでいろいろ大変な人たち同士だとコミュニケーション自動化ツールっていろいろありがたいと思うんですが」「それあるかも」「日本だと結局口頭でやりとりする方が早いみたいな感じになったりとか」

「そういえば最近Siderってツール↓使ってるんですけど、『Siderで指摘された内容は全部直してください』ってレビューで書いてあって😆、なるほどそういう使い方なんだなって思ったりしましたね」「へー、サイダー🥤」「あ、CiderじゃなくてSiderです」「もう日本語化されてる?」「Siderって元々日本発のサービスだった気がします(ウォッチ20180910)」「あ、そうなんだ!」「とにかくこういうのに任せるのもひとつの手だなと思って」

「おいくら万円?」「ユーザー単位か」「フリーもあるけどっ」「日本語で自動レビューされるならいいかな: 別に英語でもいいけどっ😆」「スタンダードが月1500円でリポジトリ数制限ないんなら、ちょっとチームで使ってみようかなという気になってきた😋」「もしGitHubでしか使えないとかだったら残念だけど」「GitHubで、と書いてあるからどうもそれっぽいし」「BPSはGitLabだからGitLabで使えたらいいんだけどな〜🥺」「GitHubとGitLabを同期するプラグインとかでできそうな気もするけど、(GitLabの)マージリクエストをプルリクエストとしてGitHubで同期してくれないとだめだし」「そっか〜」「既に方法がありそうな気もするし、1か月ぐらいやってみたいな」「Siderにしとけば『Siderの言う通り直してください』で済むから楽そうだし😆」「レビューされなくて止まっちゃうより自動レビューされる方がきっといいし😋

⚓その他Rails


つっつきボイス:「そういえばHerokuも昔といろいろ変わって、Dockerコンテナも動くようになってるし、できることも増えてたりするし」「へ〜!」


⚓Ruby

⚓rescueをちょっとカッコよくリファクタリング(Ruby Weeklyより)

短い記事なので、コードは最後のだけ引用します。

class SomeAPIService
  def self.call
    perform_action
  rescue SomeAPIAccountExpired
    send_email_with_notification_about_expired_account
  rescue SomeAPIInvalidAPIKey
    send_email_with_notification_about_invalid_api_key
  rescue SomeAPIUnauthorizedAction
    Rollbar.error('log some useful info')
  end
end

つっつきボイス:「あーなるほど、Rubyのトリプルイコール===def self.===(exception)みたいにオーバーライドするのか」「すると上のようにrescueできると」「ほーほー!これは中々😋」「トリプルイコールはクラスの一致を取れたと思うので、それを例外クラスの一致チェックに使ってると」「そこに使うのね: 言われてみればたしかに〜」「PHPとかJavaScriptやってる人からすれば、===って完全な型一致という印象だし😆」「Rubyはそこが特殊😆

⚓RubyのTracePointを探る(Ruby Weeklyより)

# 同記事より
trace = TracePoint.new(:raise) do |tp|
  p [tp.lineno, tp.event, tp.raised_exception]
end
#=> #<TracePoint:disabled>

trace.enable
#=> false

0 / 0
#=> [5, :raise, #<ZeroDivisionError: divided by 0>]

つっつきボイス:「TracePointだ」「Brandon Weaverさんはこれでしばらくシリーズ記事を書くみたいです」「TracePoint、知っておくと便利だけど使う機会が年に1度あるかないか🤣」「🤣

⚓RubyのBEGIN/ENDがブロックを取れる

「Goby言語のst0012さんが、RubyでBEGINENDでブロック取れるって知らなかった〜と言ってたので」「おぉ、Rubyのこういう大文字定数にはときどきあまり知られてない機能がありますヨ🤓

「たとえばRubyにDATAってのがあるんですけど」「えっナニソレ?」「コードの中でDATAという定数を書いて、そのファイルの末尾に__END__って書くと、__END__から下にある文字列を丸ごとDATAで読み取れる↓」「それスゴい😳」「この間ちょろっと書いたNagios用のスクリプトで少し長めのERBでNagios configを書かないといけなかったんですが、そのときにこの方法でERBコードを読み込みました😎」「ヒアドキュメントよりよさそう😋」「ファイルの末尾にまとめて書けるから散らからないし🧹」「書くときに『たしかこんな機能があったはず』と思って探して見つけたし😆

# ruby-lang.orgより
sum = 0
DATA.each_line do |line|
  sum += line.to_i
end

DATA.rewind
p DATA.gets    # => "sum = 0¥n"

__END__
17
19
23
29
31

参考: constant Object::DATA (Ruby 2.6.0)

「これはRuby Gold級の技っぽいですね」「Rubyマニアじゃないとあまり知らないかも😆」「これはもっと知られてもいい機能」「Ruby Gold持ってませんでしたっけ?」「いやその、BEGINENDがブロック取れるというのは何とな〜く思い出したけどDATAは知らなかったマジで😅

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

追記(2019/02/27)

DATA__END__についての記事を以前翻訳していたのに忘れていました↓。

Ruby: 終わりのない巨大な`DATA`定数(翻訳)

⚓その他Ruby




つっつきボイス:「LSPはランゲージサーバー」「ランゲージサーバーは流行って欲しい🙏


以下はつっつき後のツイートです。

お誕生日おめでとうございます🎊!人間ならそろそろ結婚して家庭を持ってそうな年頃ですね💒

⚓Ruby trunkより

⚓Ruby 2.7にEnumerable#tallyが入るお(Ruby Weeklyより)

#11076で元々count_byという名前で提案されたのがtallyに変わったそうです。

tally: {他動-1} : 〜を勘定する、計算する、数え上げる、集計する


つっつきボイス:「タリーって?」「何でも元々count_byって名前だったらしいんですけどtallyに変わったそうです」「はは〜、どの種類の要素がいくつあるかを数えてハッシュの形で返してくれるのか!」「こういうことたまにしますもんね😆」「するする😆」「自力で書くとそこそこ面倒なヤツ」「後はこのメソッドの存在を忘れなければ😆」「Ruby 2.7から入るそうです」「文章のアルファベットでeが一番多いみたいな、ある種の『黄金虫』的な問題を解くのが劇的にラクになりそうですね😋」「名前もcount_byよりいいかも」「count_byだと引数渡して数えるみたいに思えるし」

# 同記事より
%w(foo foo bar foo baz foo).tally
=> {"foo"=>4, "bar"=>1, "baz"=>1}

参考: 黄金虫 - Wikipedia

「これって先週のウォッチにあった、期待する値を渡すとそれを返せるメソッドを返すgemにちょっと通じるところがあるような(ウォッチ)」「そういえば先週あの後amatsudaさんからツッコミもらいました↓😅: MethodFinderっていうのがあるそうです」「😆

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

⚓AWS Lambdaの最適なタイムアウト(Serverless Statusより)


同記事より


つっつきボイス:「Lambdaは最近タイムアウトが伸びて長く実行できるようになってるんですよね」「知らなかった〜」「Lambdaの登場直後は30秒ぐらいしかなかった覚えあります🕢

「なので最近のLambdaだと確か15分ぐらいまで伸びて…ん?記事を見ると最大実行時間/リクエストが5分↑?」「ちょ😆この記事昨年夏なんですけど」「ホントだ🥺」「まあAWSのこうした制限はかなり頻繁に更新されるんですよ: ↓やっぱり今は15分」「そうだったんですね😳

参考: AWS Lambda の制限 - AWS Lambda


aws.amazon.comより

「Lambdaもこうした制限が何度も変わっています: Lambdaの場合、タイムアウトが30秒しかないとたとえばクローラのように時間がかかる処理を回せない」「あ〜」「当時そういうものを動かしたければLambda関数を分解しないといけなかったんですが、それが5分になり、そして最近になって15分になったおかげで、ある程度のバッチ的な処理でもLambdaで回せるようになってきた」「なるほど!」「まあLambdaでそういう処理を動かすのはどうかと思いますが😆: あくまでたとえとして」

「とにかく、AWSのこうした制限変更というか緩和は前提条件が変わるレベルと言ってもいいくらい♟」「たしかに」「30秒が15分になればユースケースがとても広がりますし💪

「たしかAWSのELB(Elastic Load Balancing)↓なんかもそうですね: 最初の頃はやはり30秒かそこらだったんですが、それだとデカいファイルをダウンロードしたときにぶった切られるという⚔」「でしょうね〜」「それが今のALBでは、おや、タイムアウトが制限事項に入ってないか↓: デフォルト60秒で自分で1 ~ 4000秒に調整できるらしい」「ちなみに以前のはClassic Load Balancer」

参考: Elastic Load Balancing(クラウドネットワークのロードバランサー - ELB) | AWS
参考: Application Load Balancer - Elastic Load Balancing

「EC2の課金が秒単位に変わったなんてのもそうで、前はできなかったユースケースも使えるようになったりする」「たとえばどんなユースケースでしょう?」「秒単位になれば、ささやかなバッチぐらいならEC2で動かしてもやっていけますね🧐: 昔EC2が時間単位だったときは、1秒動かそうが1時間動かそうが料金同じだったので」

参考: EC2 インスタンスと EBS ボリュームで 1 秒あたりによる請求が可能に | Amazon Web Services ブログ

「制約が変わると運用のプラクティスそのものが変わるので、押さえておかないといけないヤツ」「改定があったらRSSで更新情報取れたりするんでしょうか?」「制約の改定はだいたいAWS Summitとかre:Inventのタイミングで出ますね」「あ〜なるほど、イベントのたびに更新が入る感じですか」「そう!だからAWSのイベントのたびにこの辺を追いかけておくと改定をキャッチできたりしますね😋

「ただ制約の緩和はそれ以外のタイミングでしれっと行われることもありますが😅」「AWSの制限事項ってその都度調べないと見落としそうですね」「まあはてブで追ってればだいたい見えてくるので、見逃さないようにしておけばだいたい大丈夫👍

参考: AWS Summit Tokyo 2019 | 2019 年 6 月 12 (水) 〜14 (金) | 幕張メッセで開催
参考: AWS re:Invent 2018 | アマゾン ウェブ サービス

「ちなみに元の記事はタイムアウトの話してるけどもうタイムアウト変わってるから😆」「😆」「まあ5分と15分ならそんなに大きな違いじゃないかな: 30秒と5分は大違いだけど」「いずれにしろLambdaはこの記事でチェックしているようなところは見ておきたいですね: 考え方は通用する☺

⚓.devドメイン


つっつきボイス:「そうそう、devドメイン売り始まった🍌」「github.devとか取ってるのか」「大手の会社はとりあえす取っとかないと😆」「後でそいつから高値で売りつけられる😆」「早いもの勝ち」

「bpsで.dev取れるかな?」「bpsincなら普通取れそうだけど、3文字ドメイン取れるなら」「今取っちまおうか😆」(しばしチェック)

「やったavailable」「ほあっ初期費用40万💰」「さすがにポケットマネーの範疇越えるわ😇」「bpsincみたいに長いと安くなったりしないかな?」「課金は安くなったけど初期費用変わらない…😇」「短いドメイン名は価値高い🗼

「まあ初期費用が高いのにはドメインハイエナ防止の意味合いもあるんでしょうね」「ハイエナでなくても面白半分でドメイン買われるかもしれないし」

「お、twitter.devがavailableですって」「取れば?😆」「いやいやそんな😆」「まあ今どきは明らかに本人でないドメインをたとえ取れたとしてもレジストラ側で拒否されるだろうし」「悪質なのはアカンと😈」「google.devは…使用不可」「でしょうね〜」

「40万は無理だからbabaさんにbpsのdevドメインおねだりしちゃおうかな🤣」「40万あったら一番いいMacbookをフル装備で買えちゃうし」「でもたとえドメイン買っても悪質とみなされたら取り上げられちゃうんですよね🤣」「何かのはずみでそれ食らったらキツイ😇」「まあ返金ぐらいしてくれるんでしょうけど」「ドメイン購入先の規約次第だけど返金は基本ダメなんじゃないかな〜」

その後ドメイン買うかどうしようか話がSlackで進行しました。

「もし取れたらAWSのRoute 53で管理したい」「そうすっといろいろと便利そう😋」「今使ってるドメイン管理会社の画面がイケてないし反映遅いし😅

Amazon Route 53(クラウド DNS - ドメインネームサーバー) | AWS

⚓バークレイのサーバーレスコンピューティング総括論文(Serverless Statusより)


つっつきボイス:「Serverless Statusでトップ記事だったのがこの論文でした」

  • サーバーレスコンピューティングとは
  • 黎明期
  • 今日のサーバーレスプラットフォームの制約
  • サーバーレスコンピューティングは今後どうあるべきか
  • サーバーレスの落とし穴
  • まとめと今後の予測

「ざっとアブストだけ読んでみるとサーベイ論文らしい: サーバーレスの歴史と現状をアットアグランスでひととおり見渡せる感じ」「この間のEnvoy(ウォッチ20190212)もあるかな〜そこまで新しくはないかな?: お、論文は今年2月だからめちゃ新しい」

「こういうカテゴライズいいわ〜↓」「サーバーレスコンピューティングでは通常のプログラミングと違うさまざまなリソース制約とかがあるみたいな話」「そのあたりを知るのにいい論文👍」「これはやる気ある人が集まって輪読とかやってもいいんじゃね?」「章単位ならそんなに大変じゃないかも」「じゃ社内の英語強い勢を巻き込みますか😆」「技術論文だけに専門用語の嵐だから英語力よりコンピュータサイエンスの基礎用語を押さえてる人が欲しい感じ😆


同論文より

⚓Docker創業者のお別れブログ記事


つっつきボイス:「最後はDockerの創業者がおさらばしたというブログ記事です」「割と言葉のキツイ人だったような気が😆: DockerがrunCになったときにも放言してTwitterとかからツッコまれまくったような覚えが」「runC?」「まあDockerのコアがオープンソースになってみたいなヤツ」

参考: はじめてのrunC - Qiita

「今ってDockerのお株が他にどんどん奪われてる感じじゃないですか」「今のDockerはどちらかというと設計全体をDocker化するという位置付けですが、元々は単にコンテナを動かすランタイムだったんですよね: そこをrunCに取られたらもう何も残らないという😆」「Docker社自体もマネタイズの機会を逃しつつあったりするし創業者としてはツラいでしょうね」「といってrunCの流れに逆らっていたらDockerはこんなに流行らなかっただろうし」「オープンソース界隈とうまくやりとりできる人じゃなかったのかななんて😆」「Redisのマネタイズの件もざわついてましたね」「この辺はいろいろ難しいな〜」

「今Docker Hubを有料で使ってる人ってほぼいないっしょ?」「Docker SwarmもKubernetesにお株奪われてる感あるし、どこで儲けを出したらいいんだみたいな」


hub.docker.comより


docker/swarmより

「Docker EEはDocker社がメンテしてると思ったけど、通常のDockerとの違いよくわからない😆」「改めて見てみると↓、認定されたインフラ/プラグイン/ISVコンテナが使えるとかか」「他のソフトウェアでもできるものが多い感😆

参考: Dockerが商用版Dockerとして「Docker Enterprise Edition」発表、認証済みイメージやプラグインなど提供。無償版は「Community Edition」に - Publickey

「ちなみに上の記事はFacebookで見かけたんですが、そこで話題になってたのが『このヨットの絵、おかしいだろ』↓の方でした」「すごくどうでもいい話😆」「私ヨットのこと全然知らなくてどこが変なのかわかりませんでした😅」「これじゃ前に進まないだろみたいな😆


同ブログより

⚓その他クラウド



つっつきボイス:「卜部さんのツイートが目に止まったので」「黙ってマイク内蔵でしかもOTAで有効、そりゃエグすぎる😇」「OTAって?」「On The AirOver The Airなので要はネット越しに音取り放題🧐」「ひょえ〜😭」「それを隠し機能とかもう何というか」

参考: OTA: 用語解説辞典|【公式】NTTPC

⚓SQL

⚓PostgreSQLのfsyncバグ


つっつきボイス:「あーこれ見た見た!」「ファイルシステムごとの挙動の違いを見落としてたとかそんな話」「FreeBSDとzfsなら大丈夫っぽい」「まあfsyncが失敗するようなユースケースはオンプレミスではほぼありませんけどね: その代りNFSみたいなネット越しだと普通に失敗しますが😇

参考: Network File System - Wikipedia

⚓Rails開発者にとってSQLを学ぶのが重要な理由7つ


つっつきボイス:「Everyday Railsの記事です」「SQLが重要なのはもうあったりまえの話☺」「そういえばこの間のSQLQL話(ウォッチ20190212)の『Railsエンジニアはもっとデータベースを大事にすべき』で、はてブで誰かが『ごめんなさい』とコメントしてました」

「実際、Railsがどれだけ進化しようとデータがRDBに入っている限りRailsとRDBの間の通信はSQLになるんだから当然大事😤」「Railsがいくら速くてもSQLが遅かったら遅くなるし😆」「開発が極まってくればいずれ生SQLを書くことになるんだし😆」「そうそう、SQLを書ける能力はいざというときにとっても必要」

⚓その他SQL


つっつきボイス:「これは久しぶりに私には翻訳不可能と思った記事でした: データベースを無理やりバレエの用語になぞらえてみたそうですが、バレエの用語や素養が私にまったくないので面白みを理解できないという😅」「これは英語が読めてもわけわかんない🤣」「SQLの構文をバレエに絡めてるっぽい: これの次はこれみたいに順序が決まってるとか」「これはもうネタ記事☺


同記事より

だいぶ以前ですが、教育テレビでたまたま見かけたジュリアーノ・ペパリーニという男性バレリーナ(男性だからバレリーノ?)に思わず見入ってしまったのを思い出しました。バレエもダンスもわかりませんが、彼がくるくる回りながら登場しただけで、まるで星屑を振りまいてるような錯覚すら感じました。今は舞台演出家として大成功しているらしいのですが、彼自身のバレエ動画がネットにさっぱり見当たりません😢

GIULIANO PEPARINI – SHOWREEL from giuliano peparini on Vimeo.

参考: Giuliano Peparini - Wikipedia — イタリア語🇮🇹ですが

⚓JavaScript

⚓Trusted Web Activities(TWA)とKotlin/NativeとFlutter

今日行われたBPS社内勉強会での「DroidKaigi 2019」報告会でFlutterやKotlinなどとともにTWAの話題もあったので。

参考: DroidKaigi 2019
参考: PWA+TWAでウェブサイトを完全アプリ化、PWAサイトのGoogle Play登録も可能に | 海外SEO情報ブログ


つっつきボイス:「今日の勉強会所要で出席できなかったけどTWAの話題とか聞きたかったんですよね😅」「勉強会ではkazzさんがとても的確な質問をしてくれてましたよ: morimorihogeさんもTWAが気になってたって: DroidKaigiに行った人はぶっちゃけTWAはあんまりマークしてなかったそうですが😆」「だ、誰か書き起こしを😆」「今回YAMAHAのいいスピーカーフォンを録画に使ったので音声クリアだと思います😋

「うぅ、この色合い↓、Windowsノートだと目がおかしくなりそう😵」「Macだともうちょっとオレンジっぽいような?」(覗き込んで)「あ〜これはかなり目に滲みる」「一応Adobe RGBに設定してるんだけど😅」「どことなくメルカリっぽい色合い?」「あ、スポンサーにいるし」


droidkaigi.jpより

「AndroidでJavaとか特にKotlin書いてる人たちにとっては、そこにWebエンジニアが入ってきて作業を分担できるのは結構エポックメイキングというか」「TWAやるのは主にJavaScripを書く人たちということになりますね」

「Kotlin勢としては、どちらかというとKotlin/Nativeの話題の方が気になってたようです」

参考: Kotlin/Native を Android/iOS アプリ開発に導入しよう - Qiita

「で社内のiOS勢が気にしてるのはマルチプラットフォームなGUIで、中でもFlutter」「勉強会の質疑応答では、TWAとKotlin/NativeとFlutterがさながら三すくみ的な様相を呈してました🤣」「そうそうそんな感じ🤣」「Kotlin勢はDartで書きたくない、とか🤣」「Dartは自分もちょっとね〜🤣

Flutter: 標準ウィジェットをコピーして改造する

参考: Dart - Wikipedia
参考: Dart 2 | Dart — 昨年2が出てたんですね


dartlang.orgより

「Kotlin/Nativeってだいぶ前からやってたはずだけど、今どのぐらい進んでるんだろう?」「勉強会の報告ではいろいろ策定中という話だったので、まだproductionで使うほどではないのかなとも思ったり」「ライブラリもまだ足りないらしいですね」

「まあWeb開発チームとしては、ガワネイティブで食えるようになったらそれはそれでデカいんじゃね?と思ったりして😆」「かもですね😆: 見た感じですが、TWAに乗るよりFlutterの方がよさそうなのかなとちょっと思ったり」「FlutterはReactとほぼ同じ感じで書けるからって強調してましたね」「Flutter勢からの勧誘😆」「でもFlutterはDartですよね😆」「Dart自体がJavaScriptの置き換えを目指してる言語だし、まあどうにかなるかななんて😆

「この場合ガワネイティブってどう理解したらいいでしょうか?」「要するにWebViewが入ってるだけのアプリ」「ガワだけネイティブ🍩」「WebViewが立ち上がると後はWebViewの中でWebアプリケーションを動かすイメージ」「ちょっとElectron的な」「AndroidアプリなりiOSアプリの形になってないとアプリストアで配信できませんしね」

参考: WebViewとは - IT用語辞典


electronjs.orgより

「まあガワネイティブがReact Nativeと同じなの違うの?って聞かれたら違うんだけど😆: React NativeはJavaScriptでReact書いたらそれがネイティブアプリになるというヤツだし」「でもガワネイティブの方が歴史は古いという🕋」「ですよね😆」「今日の勉強会で途中からネイティブの意味がだんだんよくわからなくなってきました😆」「アプリケーションそのものはWebなんだけど、というか」「ガワネイティブでも、たとえばカメラ機能の部分だけ自分でちょこっと書いたりとかはするけど、他の部分はすべてWebViewにお任せしちゃう」


facebook.github.io/react-nativeより

「TWAだけど、ちょろいAndroidアプリみたいなのをささっと書けたらアプリの単価も取れたりして😆」「いいかも😆」「ほら、Webをアプリの形にできるとたとえばHomeボタンを無効にするとかできるし」「Backボタンとかもですね」「iOSアプリにもそういうモードがあって、それで業務用アプリとして使えるようにしたり」「その意味で、形だけでもネイティブアプリにできるのは大きいっすね😋」「それもあってガワネイティブには結構期待してる😘

「TWAとPWAって同じじゃないけど割と一緒に出てくることが増えてるっぽいですね」「PWAはAndroidとかに限らない話ですが、TWAはGoogleがやってるストアで扱うための仕組みの方ですね🧐

WebサイトをPWA(Progressive Web App)に変える簡単な手順(翻訳)

「PWAとかに投資するよりは…という気がちょっとしましたけど」「まあPWAに投資したいというより、RailsでもVue.jsでもいいのでちょろいアプリをささっとPWA化できたらいいよね、ぐらいの感じで」「ぶっちゃけそれ用のヘッダーをつければ基本的にPWAにできるし: それだけだとUIというかUXまでは最適化されないにしても」「あ〜なるほど、オフラインでは使えないにしても、アプリらしい体裁でホーム画面にボタン置くぐらいのことはヘッダというかマニフェストつけるだけでできると」「そうそう、それだけでやれるならうちらもアプリ作れますって言えるし😋」「そこまでラクにできるなら夢ありますね〜✨」「どってことないといえばそうなんだけど、PWAでWebアプリの単価上げるのやってみたい😍」「1.5倍ぐらいになったりして」

「ところで今回のDroidKaigi会場はBPSから近いのか」「発表者は2軒目のもうやんの隣とか言ってましたけど」「それって😆」「方向感覚がすべて食べ物屋ベース😆

⚓その他JavaScript


つっつきボイス:「mizchiさんのインタビューです」「mizchiさんがインタビューされてるのかと思ったらインタビューする側なのか」「フリーランスになったからこういう仕事もやってるのかな」

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

⚓WebAuthnとFIDO


同サイトより


つっつきボイス:「はてブからです」「生体認証をWebブラウザでやるみたいな感じだった」「そういえばこういうのあったあった: 認証機器の種類を問わずこのフレームワークでやれるとかそういうの」

参考: 生体認証 - Wikipedia

「いつも思うけど、認証は難しいというか、どこまでやるべきか感が半端ない」「たしかに〜」「もちろん、これが漏れたら会社潰れるみたいなのはちゃんと守らないといけないけど、それ以前にリスクの分散も必要になってくるし」

「ところで何でWebAuthnのnが最後に?」「ググって見つかりやすいようにとか?」「mattnさんみたい😆

追記(2019/02/27)

情報ありがとうございます🙇

⚓最新HTML/CSSコードスニペット


つっつきボイス:「管理画面ぐらいならこういうのでも別にいいかなと☺」「社内だとデザインチーム向けの記事ですかね」「ランディングページ(LP)ぐらいだったらこういうのをふんだんに使ってもいいんじゃないかと😆

「これとかもうスゴいな〜↓」「CSSアニメーションでやるとかスゴすぎ😳」「もう発想一発の世界😆」「これCSSだから文字を選択できるし、文字を移動しているのか書き換えているのかも一発でわかるし」

See the Pen
Flipping Out
by Jon Kantner (@jkantner)
on CodePen.

「思いつきですけど、近い将来にテキストはこんなふうに動的にするのが普通になったりするかなと: 重要な部分がプルプル震えて視認性をアップするとか」「まーなくもないかも?」「今の絵文字は色まで含んでるし、文字とはグリフとは何ぞやみたいなのもぶっ飛ばしつつあるというか☺

⚓その他フロントエンド



charlesproxy.comより


つっつきボイス:「これも今日のDroidKaigi報告会で触れられてました」「BPSのアプリチームの人が言うには、Charlesは有料だけどできがいいので使ってるそうです」「ほうほう、普通にプロキシでデバッグできるサービス: この種のツールは昔からいろいろありますが、使いやすいのが一番😋

「ちなみにこの種のツールはカスタム証明書が入れられるかどうかがポイント: それができないと話にならない」「あー」「でないとHTTPS通信をキャプチャしても暗号化を戻せないから読めない😆

「モバイルアプリはWebみたいにブラウザのデバッガーが見えないからこういうのが必要なのかなと思ったのですが」「Webでも必要なときはありますね: ここまでしなければならないほどの状況はめったにありませんが」「おー」「HTTPレベルの通信であればブラウザのdevツールで見えます: たとえば(API触ったことはないけど)WebSocketなんかで、TCPで言うurgentフラグみたいなのを使って今バッファにあるものを吐き出させるなんてときに、それが正しく動作しているかどうかを確認するにはHTTPレベルのキャプチャでは無理で、パケットレベルでのキャプチャが必要になるでしょうね」「なるほど」

⚓言語

⚓最近のPython環境構築


つっつきボイス:「Rubyでrbenvやらbundlerやらでやってるのと同じようなことをPythonでもやってるんだなと思って」「まあpyenvとかanyenvあたりでやれそう」

[Rails 5] rbenvでRubyをインストールして新規Rails開発環境を準備する

「記事ではこのツールがいいとかAnacondaは古いとか書いてあってコメントでツッコミがあったりしてますね」「Anacondaというと自分はRed Hatのインストーラの方を思い出すし↓」「自分もそれ聞いたことあったし」「リンク先はGUIだけど、CUIのcurses的なAnacondaもあった気がする」「PythonのAnacondaは機械学習で入れがちなライブラリだったと思うんですが、jupyter notebookあたりと一緒にインストールしたときに入ってきた覚えがあります」

参考: 第13章 Anaconda を使用したインストール - Red Hat Customer Portal
参考: curses - Wikipedia

「ところで採用面接なんかでも最近Pythonかじってる人がだんだん増えてきた感」「自分も最近触るはめになってます😅」「流行りの言語でもあるし」「業務でどこまで使うかだけど🤔

「Pythonでビジネスロジック書いてるって話はなぜかあまり聞かない」「そういえば」「機械学習とかAIは基本Pythonだろうけど、WebアプリとかになるとDjangoとかやってる話とか見聞きしてもそっちはあまり聞かないし」

「以前雑談してたときに聞いた話なんですが、以前Pythonを死ぬほど書いてた人が今はRubyがいいと言ってて、その理由がPythonは彼にとっては走らせてみないと挙動がわからないからということでした: Rubyは見ればだいたい挙動がわかるし、うんと小さいコードなら書いていきなりpushとかすることもたまにやったりするとも言ってました」「一般にはPythonの方がコードのブレが少ないって聞きますけど、意外ですね」「そうそう、Pythonの方が誰が書いても同じようなコードになるとか聞くし」

「自分の印象だと、Pythonはあんまりクラスのこととかうるさく言わない感じがしました」「オブジェクト指向という意味ではRubyの方が読みやすいとかそういうところがあるのかしら?」「自分のPythonの使い方がシェルスクリプトに毛の生えたようなものなので😅

「何となくだけど、PythonはAtCoderみたいなプロコン的なところには向いてる気がちょっとした: 解くべき問題がはっきりしてて、かつ制限時間内に解くみたいな状況」「あー、一人でできる規模で、設計とかあまり関係ないところとか」

参考: AtCoder

「まあ今どきは大きいものについてはオブジェクト指向設計でやるのが当たり前ですが、オブジェクト指向設計の何がいいかというと、忘れられるところ🤣」「🤣」「自分が使ってるものの内部を特に気にせず使えるのがいいところ😋

⚓その他言語


つっつきボイス:「Matzから若い人へのアドバイス的な」「自分のやってきたことは再現性がないのであまり参考にならないけどみたいな」「上のツイートの前には以下がありました↓」「4番目は笑い取ろうとしてますね😆」「きっとそう😆

「Rubyについては、たしかにタイミングの要素はとっても大きかったと思うし: みんながPHPで辟易してて、かつフレームワーク戦争の終息が見えなかった、まさにちょうどそのときにRailsが出現したので」「あの当時3つぐらいフレームワークが乱立してましたよね」「自分も3つぐらいしょうがなく使ってたし: 何でDAOをいくつも使わなきゃいけないんだとか😅

「それがRails出現後はいっせいにRailsっぽいフレームワークに落ち着きましたし😆」「そうそう!何だよ結局Laravelかよって😆」「Railsが出現したことで仲の悪いPHPフレームワーク同士の結束が急に強まったというか😆」「今のPHPフレームワークならちょっと触ってみてもいいかな〜なんて😆

「今のPHPは型ヒント付けられるし、PHPをstrictモードで使うなら割と頑張れる気がする」「お〜」「Rubyと違って引数の型を書ける」

参考: PHP: 関数の引数 - Manual — 型宣言(タイプヒンティング)
参考: PHP: 新機能 - Manual — strictモード

「D言語ちょっぴりやったことある😆」「言語の登場の早い遅いとかちょっとしたタイミングで運命が変わるってたしかにあるな〜: WebObjectsも早すぎる登場だった感あるし」「JavaScriptも一度滅びかけたのが完全に甦ったし、あのObjective-Cなんてのも当時滅びるしかないと思ってたらNeXTで全面採用されて今やiOSまでObjective-Cだったりするし😆」「ほんと先のことはわからん😆」「ジョブズがあのときその選択を取らなかったら今絶対残ってなかったろうし、その意味では彼がObjective-Cを復興させたといっても過言ではないし」

参考: WebObjects - Wikipedia
参考: Objective-C - Wikipedia

「そういえばObjective-Cの本って、すごく古い本とすごく新しい本のどっちかしかない🤣」「間がごっそり抜けてる🤣」「NeXTの時代とiOSの時代」「Objective-Cで最大にキモいのはCと完全コンパチなところ」「そうだった!」「Objective-CはC言語のプリプロセッサだから、実際にはCのコードを出力するし」「知らんかった〜」「Objective-Cの構文キモチワルイ🤢」「(指で角かっこ[ ]作って)これですよね?」「それそれ😆」「名前付き引数あるのに順序が決まってるとか」「なに〜😆」「それ名前付き引数なんでしょうか😆」「Cとの互換性を保つためなのでしょうがない🧐

// Wikipediaより
val = [obj1 msg: [obj2 msg]];

⚓その他

⚓ATMも顔認識で


つっつきボイス:「そういえばセブン銀行も最近ATMの利用率が下がってるせいで手数料収入減ってツラいらしいし😆」「たしかに現金利用する機会減ってますし」

⚓MacでEXE


つっつきボイス:「MacだとたまにEXEがzipに入ってても安心しきっちゃってたし」「してた〜」「いや安心はしないけどっ😆

⚓その他のその他

参考: 誰でも簡単に「縫合が必要なレベルの切り傷」に応急処置を施せる「ZipStitch」 - GIGAZINE


つっつきボイス:「この傷本物なんでしょうね(イタソー)」「プロレスラーが接着剤で応急処置してから病院に行くみたいなのよりはまともそう?」「これってインシュロック(結束バンド)みたいだし」「インシュロックだ!」「アウトドアで欲しいヤツでしょうね」

参考: よくあるインシュロックの誤使用例 |ヘラマンタイトン株式会社


参考: 実在しない人の顔写真を無限に生成できるWebサイトが公開。ディープラーニング技術を応用 - Engadget 日本版


つっつきボイス:「実在しないっていうのは統計的にそうだろうと言ってるだけなんでしょうね」「混ぜてるから実在しないとかそういう感じかと」「実在しないことを直接バリデーションできたらスゴいし😆」「悪魔の証明😈

参考: 悪魔の証明 - Wikipedia

⚓番外

⚓ウミヘビ

⚓宇宙2題


同サイトより


同サイトより


つっつきボイス:「1つめは大学のときの知り合いが趣味で作ってるはやぶさの軌道を追いかけるサイトなんですが、何年も前にNASAから(別のアプリですが)『これ使わせてくれ』って言われたという何だかスゴい人です」「ほぇ〜」

「2つ目はページのナビゲーションが見事だったので: とにかくページを下にスクロールしてみてください」「ほっほ〜☺


今回は以上です。

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

週刊Railsウォッチ(20190212)EnvoyとIstioに大注目、SQLQLとは、buildkite.comのCI、さよならItanium、PWA vs Androidほか

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

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

Rails公式ニュース

Ruby Weekly

Hacklines

Hacklines

WebOps Weekly

webops_weekly_banner

Publickey

publickey_banner_captured

DB Weekly

db_weekly_banner

Serverless Status

serverless_status_banner

ペーストが禁止されているHTMLフォームに無理やりペーストするワンライナー

$
0
0

会員登録フォームなどで、たまにクリップボードからのペースト(貼り付け)が禁止されているサイトがあります。

貼り付けを実行しても何もおきない

これは不便なので、強引にペーストしてしまおうという内容です。

注意点

本記事で紹介している手法は、開発者ツールやJavaScriptの実行を伴うものです。
一般論として、「開発者ツールでこう操作すると良いよ」「このJavaScriptを貼り付けると良いよ」という記事を理解せずに鵜呑みにすることは、高いセキュリティリスクを伴います。利用の判断は慎重にお願いします。

なぜ困るのか

パスワードは通常パスワードマネージャや適当なジェネレータで生成すると思います。

英数記号を含む20文字以上の文字列を、1文字ずつ Il1 を間違えないように気をつけながら心を込めて打ち込むのも趣がありますが、忙しいときにはそんなことはしたくありません。パスワードマネージャのauto type機能を使えれば良いですが、

  • 僕が常用している KeePass はそのあたりの使い勝手や柔軟性がそこまで良くない。
  • ログインフォームならともかく、会員登録フォームはパスワード確認欄などもあり、全自動で良い感じに入力してくれないケースのほうが多い。一度きりのために設定を書くのも無駄。
  • 現在フォーカスしているフォームにパスワードだけ単品で入力する機能を使えば良いが、ショートカットキーを間違えてEnterまで入力してしまうほうが実行されると(うっかりフォーカス間違えていたらどうしよう)怖い。

などの理由で、ログイン時はブラウザアドオンの自動入力を使い、それが使えないケースではクリップボードを利用しています。

クリップボードからのペーストが禁止されると、1文字ずつ手入力を強いられることになり、疲れる上に入力ミスでログインできない事故も多発します。

なぜ禁止されているのか

メールアドレスの確認欄については、「入力欄からコピペできることで、間違ったメールアドレスをコピペして登録してしまう人が多くなる。禁止すべきだ。」という意見が通っていた時代もあると思います。現代では、間違える人は何しても間違えるので、間違えたら再登録すれば良いし確認欄いらなくねというのが主流だと思います。

パスワードのペーストを禁止するサイトがある理由は、すみませんよくわかりません。なんとなくペーストはけしからんみたいなセキュリティチェックリストでもあるのでしょうか。

禁止されている仕組み

いくつかの方法があると思いますが、一般的に利用されているのはJavaScriptで paste イベントを preventDefault() する方法だと思います。「JavaScript 貼り付け 禁止」などでググれば似たようなものがたくさんヒットします。

document.querySelector('input').addEventListener('paste', function(ev) {
    ev.preventDefault();
});

これは「貼り付け」イベントが起きたら「標準の挙動(フォームに文字列を貼り付ける)を停止する」という処理です。

解除方法(手動)

ChromeではdevtoolsからEvent Listenerを削除できます。

貼り付けたいフォーム上で右クリック→「検証」ボタンを押すとdevtoolsが起動するので、Event Listenersから paste を探します。サイトによってはフォームそのものではなく親要素などに設定されていることもあります。

見つけたら、マウスオーバーすると出てくるRemoveボタンをクリックするだけです。

該当のEvent ListenerをRemoveボタンで削除する

もう少し省力化してみる

毎度GUIで操作するのは手間です。そこで、ブックマークレットにしてみましょう。

Chromeでは、以下の文字列をアドレスバーに貼り付けたり、ブックマークに登録しておいて呼び出すことで、同じような効果が得られます。

javascript:document.addEventListener('paste', e => e.stopPropagation(), true)

Firefoxではアドレスバーへの貼り付けが動作しないようなので、開発者ツールのコンソール上で以下を実行するなどします。

document.addEventListener('paste', e => e.stopPropagation(), true)

数多くのサイトで試したわけではないので、動かないケースも多々あると思います。その時は適宜調整が必要です。

おたより発掘

週刊Railsウォッチ(20190701)RMagickのメモリ使用量が劇的に改善、インスタンス変数の定義順で速度が変わる?、GitLab CIランナーをローカルで回すほか

$
0
0

こんにちは、hachi8833です。kazzさんのアバター画像が変わったことにお気づきでしょうか。

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

今回のつっつき録画に音声が入っていなかったので、今回は分割せず軽量版とします。申し訳ありません🙇

お知らせ: 第12回公開つっつき会(無料)

開始以来ついに1年目を迎える第12回目公開つっつき会は、今週7月4日(木)19:30〜にBPS会議スペースにて開催されます。引き続き皆さまのお気軽なご参加をお待ちしております🙇。

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

スキーマダンプのstringをintegerに変更

# activerecord/lib/active_record/migration.rb#L1072
    def get_all_versions
-     if schema_migration.table_exists?
+       schema_migration.all_versions
        schema_migration.all_versions.map(&:to_i)
      else
        []
      end
    end
...
    def load_migrated
-     @migrated_versions = Set.new(@schema_migration.all_versions)
+     @migrated_versions = Set.new(@schema_migration.all_versions.map(&:to_i))
    end
# activerecord/lib/active_record/schema_migration.rb#L47
      def all_versions
-       order(:version).pluck(:version).map(&:to_i)
+       order(:version).pluck(:version)
      end

ここは変更すべきと思われるが、既にRC版に入っているので6.0-stableではやらない方がいいかと思い、アプリ側の変更を必要としない変更だけに留めようとしてみた。
これは、すべてのスキーママイグレーションのバージョン番号をintegerとしてダンプしていた#36439の一部を元に戻す。この変更は振る舞いが変わらないので別段問題ではない。6.1で改めてこれを非推奨化するべき。
cc/ @rafaelfranca そちらから本変更を求められ、structureファイルが変更されている。大きな問題ではないので、GitHubの現在の更新はそのままでよいと思うが、この振る舞いの変更の方が心配なので、いったんロールバックして6.1で改めて非推奨化しようかと思う。
同PRより大意


つっつきボイス:「プルリクメッセージを見ると、これは修正すべきだけど6.0-stableじゃない方がいいかもとあったのが、Oracleアダプタやってるyahondaさんがこの修正欲しいと言って入ったようです」「to_iする方が長さが短くなってよさそうではあるけど、他に何か不都合があったのかも?🤔」

init_withのスキーマキャッシュをdeep_deduplicate

# activerecord/lib/active_record/connection_adapters/schema_cache.rb#L37
      def init_with(coder)
-       @columns          = coder["columns"]
-       @columns_hash     = {}
-       @primary_keys     = coder["primary_keys"]
-       @data_sources     = coder["data_sources"]
-       @indexes          = coder["indexes"] || {}
-       @columns          = deep_deduplicate(coder["columns"])
+       @columns_hash     = @columns.transform_values { |columns| columns.index_by(&:name) }
+       @primary_keys     = deep_deduplicate(coder["primary_keys"])
+       @data_sources     = deep_deduplicate(coder["data_sources"])
+       @indexes          = deep_deduplicate(coder["indexes"] || {})
        @version          = coder["version"]
        @database_version = coder["database_version"]
      end

つっつきボイス:「deep_duplicate?」「いえ、deep_deduplicateですね」「ででゅぷりけーと!」「新しいみたいでググっても出てきません😢」


同コミットより

「GitHub上で例のコードジャンプ↑が効くぞ😋:なるほど」「-valueって何だっけ?」「stringをfreezeするショートハンドですね」「あ〜そうそう」

        def deep_deduplicate(value)
          case value
          when Hash
            value.transform_keys { |k| deep_deduplicate(k) }.transform_values { |v| deep_deduplicate(v) }
          when Array
            value.map { |i| deep_deduplicate(i) }
          when String, Deduplicable
            -value
          else
            value
          end
        end

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

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

robotstxt.orgのURLを修正

# guides/source/configuring.md#L1543
To block just specific pages, it's necessary to use a more complex syntax. Learn
- it on the [official documentation](http://www.robotstxt.org/robotstxt.html).
+ it on the [official documentation](https://www.robotstxt.org/robotstxt.html).

つっつきボイス:「ドキュメントの小さな修正ですが、robotstxt.orgっていうのがあるって初めて知ったので」「ほほ〜☺️」

参考: The Web Robots Pages


robotstxt.orgより

参考: 結局「robots.txt」ってなに?使う理由と基本の仕組みを解説|ferret [フェレット]

uniquenessでない場合にsave!の失敗がエラーにならない問題を修正

#35528の修正。
#35528は、親レコード1件を#save!するときに関連する子レコードがuniqueness制約に違反した場合にのみ発生する。これまでのところ、presenceやinclusionなどの他のバリデーションが失敗した場合はActiveRecord::RecordInvalidが(期待どおり)発生するが、uniquness制約が失敗した場合はActiveRecord::RecordInvalidが発生せず、単にnilが返されてしまっていた。
なお、トランザクションをサイレントにロールバックする機能はこの修正の影響を受けず、期待どおり動作する。
同PRより大意


つっつきボイス:「は〜こんなバグがあったとは😳」「トランザクションをサイレントにロールバックする機能というのがあるんですね」「この修正はテスト↓が重要な部分かな😋」

# activerecord/test/cases/autosave_association_test.rb#L1702
+ test "rollbacks whole transaction and raises ActiveRecord::RecordInvalid when associations fail to #save! due to uniqueness validation failure" do
+   author_count_before_save = Author.count
+   book_count_before_save = Book.count
+
+   assert_no_difference "Author.count" do
+     assert_no_difference "Book.count" do
+       exception = assert_raises(ActiveRecord::RecordInvalid) do
+         @author.save!
+       end
+
+       assert_equal("Validation failed: Published books is invalid", exception.message)
+     end
+   end
+
+   assert_equal(author_count_before_save, Author.count)
+   assert_equal(book_count_before_save, Book.count)
+ end
+
+ test "rollbacks whole transaction when associations fail to #save due to uniqueness validation failure" do
+   author_count_before_save = Author.count
+   book_count_before_save = Book.count
+
+   assert_no_difference "Author.count" do
+     assert_no_difference "Book.count" do
+       assert_nothing_raised do
+         result = @author.save
+
+         assert_not(result)
+       end
+     end
+   end
+
+   assert_equal(author_count_before_save, Author.count)
+   assert_equal(book_count_before_save, Book.count)
+ end

番外: elsif

# actionview/lib/action_view/template/resolver.rb#L272
      def extract_handler_and_format_and_variant(path)
        pieces = File.basename(path).split(".")
        pieces.shift
        extension = pieces.pop
        handler = Template.handler_for_extension(extension)
        format, variant = pieces.last.split(EXTENSIONS[:variants], 2) if pieces.last
        format = if format
          Template::Types[format]&.ref
-       else
-         if handler.respond_to?(:default_format) # default_format can return nil
-           handler.default_format
-         else
-           nil
-         end
+       elsif handler.respond_to?(:default_format) # default_format can return nil
+         handler.default_format
        end

つっつきボイス:「以下のツイートで見つけたんですが、elseifが続いていたのをamatsudaさんがelsifに変えていました」「あ、たしかにこれはelsifの方が簡潔に書ける!」「うまい具合にnilも書かなくて済みますね」「コミットメッセージのタイトルが⛳なのはコードゴルフ?😆」

参考: コードゴルフ - Wikipedia

「まだオープン中ですが、それに関連して以下のRuboCop設定更新もリクエストされてました」「IfInsideElseは入れてもいい気がするし😋」「ちょい厳しいかもという声もありますね」

「でRuboCopの方では、これに絡んで後置のifをありにできるオプションをリクエストしてて、こちらはマージされてました❤️」

参考: Class: RuboCop::Cop::Style::IfInsideElse — Documentation for rubocop (0.71.0)

追記(2019/07/02)

情報ありがとうございます!🙇

Rails

active_record_in_cache gem


つっつきボイス:「神速さんの作ったgemですが、Rails.cacheを初めて知りました😅: これはビューのキャッシュとかとは違うんでしょうか?」「Rails.cacheはクエリをキャッシュしたりできるヤツだったかな」「ほんとだ↓」

参考: Rails のキャッシュ: 概要 - Rails ガイド

ビューのフラグメントをキャッシュするのではなく、特定の値やクエリ結果だけをキャッシュしたいことがあります。Railsのキャッシュメカニズムでは、どんな情報でもキャッシュに保存できます。
railsguides.jpより

参考: Rails.cacheについて | 酒と涙とRubyとRailsと

Ruby on Railsで特定の値やクエリ結果をキャッシュするしくみとしてRails.cacheを紹介します。
この機能を使うとや有効期限を設定したり、キャッシュ内容を圧縮できます。
morizyun.github.ioより

「日本語記事に『中身は3行』とあるけどgemのコアは本当に3行だけなんですね↓😳」「はは〜なるほど、メモリに乗れば次からはデータベースを叩かなくて済むと: 記事ではinclude ActiveRecordInCache::MethodsApplicationRecordでやってるけど、自分なら特定のモデルでピンポイントにやるかな〜」

# 同記事より
def in_cache(column = :updated_at, options = {}, &block)
  value = block_given? ? all.instance_exec(&block) : all.maximum(column)
  name = "#{all.to_sql}_#{value}"
  Rails.cache.fetch(name, options) { all.to_a }
end

RMagickのメモリ使用量改善


つっつきボイス:「@Watsonさんのこの記事スゴいなと思って」「お、これRubyKaigi 2019で発表してたヤツだし!」「そういえば見たって言ってましたね(私見られなかった😢)」「あれはとてもいい発表でした❤️」

「上の記事によると、RubyKaigiの時点でリークは修正されたけどGCがなかなか発動しなかったので今回はRMagickとImageMagickの両方に手を入れたそうです」「おお〜、以前のメモリ使用量は青で、修正後は赤↓、まるで違うし」「ものすごい改善ですね💪」「今までは鍋の底に大穴開いてた感😆」「お風呂の栓がちゃんとしまってなかったというか😆」


同記事より

「以前なりゆきでImageMagickとMiniMagick使ったけど、画像系はいろいろ大変でしたよ〜😅」「そういえば記事書いてましたね↓」

[Rails] MiniMagickでPDFのページ数を取得するときはフォントエラーに注意!

GitLab CIランナーをローカルで回す


つっつきボイス:「GitLab CIランナーをローカルで動かすという少し前の記事なんですけど、ローカルでCI回せるとうれしいものでしょうか?」「おお、そりゃもちろん😍: GitLabにpushした後さんざん待たされた末にエラーとかつらいし😭」「GitLab CIランナーをDocker化してローカルで動かすか、以下からランナーのバイナリを取ってきて動かせばやれるみたいです😋」

参考: docs/install/bleeding-edge.md · master · GitLab.org / gitlab-runner · GitLab

なお記事の最後に、ローカルだとジョブのキャッシュが保存できないらしく、毎回スクラッチで回るので注意とあります。それでもローカルだと速いとも。

GitLab自社運用のための注意点とノウハウ(2018/06版)

リードレプリカのテスト


つっつきボイス:「@kamipoさんの記事ですけど、これと似たようなことってある気がして↓」「こういうつらみ、ありますね〜😢: キャッシュが効いているかどうかのテストの難しさとか思い出しちゃう😇」

あたかもマスターで更新が起きたっぽいときにリードレプリカにも更新が伝搬しているかのように見せかけるため、Active Recordはテストのときデフォルトですべてのコネクションプールをマスターのコネクションプールにすり替えるということで対処している。コミットが起きないんだったら全部マスターに接続してテストすればいいじゃないってやつです。しかしこれをされるとマスターではなくリードレプリカからデータを読んでることをテストしたいときにめっちゃこまるという話である。
同記事より(強調は編集部)

Ruby

Sorbetについて


つっつきボイス:「Steep gemをやっているsoutaroさんのブログです」「ああ、サブタイピングの困難とかsigのオーバーライドの問題とか、いろいろわかりみあります」「Rubyの柔軟さを保ちながら型チェックでカバーするのって大変そう…」

以前RubyKaigi 2019のレポート記事↓にも書きましたが、Rubyの型チェックはLevel-1とLevel-2に分けて進められていて、SteepはSorbetやRDLと同じくLevel-2に該当するそうです。

キーワードで振り返るRubyKaigi 2019@博多(#1)

インスタンス変数のパフォーマンスを調べてみた(Hacklinesより)


tendelovemaking.comより


つっつきボイス:「@tenderloveさんの記事なんですけど、インスタンス変数の定義順序で実行速度が変わることがあるみたいです」「マジで?😆」

何らかの理由で、インスタンス変数を逆方向に定義する方が、インスタンス変数を順方向に定義するよりも高速です。この記事ではその理由について説明しますが、さしあたってパフォーマンスの高いコードが必要なら常にインスタンス変数を逆方向に定義してください(ジョークにつき実際にはやらないでね)。
同記事より大意(強調は編集部)

「アニメーションGIF↓まで作ってくれてます🙏」「何というカリカリチューニング: まあそもそもインスタンス変数ってこんなにゴロゴロ作るもんじゃないし😆」「やっぱり😆」「それにしてもtenderloveさんよくこんなの見つけたし😳」


同記事より

DB

私家版「SQLスタイルガイド」(DB Weeklyより)


つっつきボイス:「opinionatedとあるので独断と偏見のSQLスタイルガイドということだそうです」「ほほ〜☺️」

「しょっぱながいきなり『SQL句は小文字で書け』?」「え〜そうかな〜?😅」「『Shiftキー押し続けるのがつらいから』みたいです😆」

-- Good
select * from users

-- Bad
SELECT * FROM users

-- Bad
Select * From users

「他のは『行頭にカンマ置くな』とか割と初歩的なスタイルへの言及に終止してますね☺️」

「『引用符はシングルクォートを使え』?」「むむ、PostgreSQLだったか、シングルクォートとダブルクォートで意味がちょっと違ってた気がするナ🤔」

-- Good
select *
from users
where email = 'example@domain.com'

-- Bad
select *
from users
where email = "example@domain.com"

(ひとしきりググる)「そうそうこれ↓」「あ〜ほんとだ!😳」「むしろMySQLが例外だったとは…」

参考: えっ、まだPostgreSQLで「”」使ってるの? - Qiita

PostgreSQLなどの標準SQLでは、
* シングルクォーテーションで囲う:文字列定数として扱う
* ダブルクォーテーションで囲う:カラム名として扱う
という仕様になっている。
(中略)
しかしMySQLだけは独自の仕様を持っています。MySQLでは、
* シングルクォーテーション「’」で囲う:文字列定数として扱う
* ダブルクォーテーション「”」で囲う:文字列定数として扱う
* バッククォート「`」で囲う:カラム名として扱う
という仕様になっています。
Qiita記事より

「もうひとつ見つけましたけど↓、こっちではSQL標準でそうなってるとありますね😳」

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

二重引用符はテーブル名やフィールド名に用いられるが、省略できることもある。一重引用符は文字列定数に用いる。これはSQL標準である。質問文のクエリを冗長に書くと次のような感じになる↓。
stackoverflow.comより大意

select * from "employee" where "employee_name"='elina';

後でSQL標準の該当箇所を探してみたのですが、うまく見つけられませんでした😇。

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

この頃人気のCSSプロパティなど


同サイトより

「アンケートベースのCSS調査結果サイトで、表示とかめちゃめちゃ凝ってます」「Bootstrapの知名度つえ〜😆」


2019.stateofcss.com/technologies/css-frameworks/より

「GridとFlexboxは知名度は同じぐらいだけど実際に使われてるのはFlexboxっぽい」


2019.stateofcss.com/features/layout/より

参考: 2019年、CSSのプロパティ・機能やツールについて使用状況や認知度を徹底調査 -The State of CSS 2019 | コリス

というわけで、以下の実測データサイトと比べてみるとまたよいと思います。なおFlorianとは京都在住のW3Cのメンバーのことです。

参考: Chrome Platform Status


chromestatus.com/metrics/css/popularityより

番外

ナノグラフェン


つっつきボイス:「グラフェンって炭素なので、将来は炭素でLSIを作れるようになるかも?」「夢ある〜🥰」「30年ぐらいかかりそうですけど😅」

参考: ナノグラフェン - Wikipedia
参考: “夢の物質” 炭素素材の製造技術の開発に成功 名古屋大学 | NHKニュース

以下の動画は上とは別のグラフェンナノリボンの紹介です。


今回は以上です。

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

週刊Railsウォッチ(20190625-2/2後編)「Webpack入門」は秀逸、「システム設計入門」、Envoy Mobile登場、Docker Desktop for WSL 2ほか

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

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

Rails公式ニュース

Hacklines

Hacklines

DB Weekly

db_weekly_banner

Viewing all 101 articles
Browse latest View live