このブログをHTTPS化した

はてなブログで独自ドメインを当てているときもHTTPS化できるようにとっくになっていたので、このブログもHTTPS化しました。

手順

こちらの記事を全面的に参考にさせてもらいました。

blog.jnito.com

まずはgemをインストールします。config.yml の準備もいい感じにやっておきます。

$ gem install hateblo_mixed_contents_finder

次に、mixed contentsが存在する記事を洗い出します。

$ hateblo_mixed_contents_finder validate_all
$ cat result.txt | cut -f 1 | sort -u > invalid_entries.txt # mixed contentsを含む記事のURLリストを出力する

ツールが invalid_entries.txt を参照してくれるので、再更新をかけます。

$ hateblo_mixed_contents_finder update_all

ふたたびmixed contentsがないかを確認します。

$ hateblo_mixed_contents_finder validate_all
$ cat result.txt | cut -f 1 | sort -u
http://blog.kymmt.com/entry/201601_read_booksq
http://blog.kymmt.com/entry/201602_read_books
http://blog.kymmt.com/entry/201603-read-books

ここまでやって、mixed contentsが残った記事のURLリストを出せたので、これらの記事については手作業でmixed contentsを取り除きました。具体的には、ベタ書きされたURLのプロトコルがHTTPだったので、次のように対応しました。

  • httphttps に置き換え
  • Amazonの画像URLは http://ecx.images-amazon.comhttps://images-na.ssl-images-amazon.com に置き換え

もう一度mixed contentsがないか確認たところ、そのような記事は見つかりませんでした。

$ hateblo_mixed_contents_finder validate_all
# ...
OK💚

最後に、はてなブログの設定画面からHTTPS配信を有効にしておしまいです。

『なるほどUnixプロセス』を読んだ

読みました。

tatsu-zine.com

どんな本か

主にRubyの Process モジュールを使いながらUnixプロセスについて知る本です。

Rubyでには Process モジュールを通じてUnixプロセスを操作することができます。つまり、プロセスに関するUnixのシステムコールをRubyから扱うことができます。この点で、Cから直接システムコールを発行するよりRubyプログラマにとっては読みやすいコードで、Unixプロセスについての解説を読むことができます。実際、Ruby製のWebアプリケーションでよく使われるResqueやUnicornも、Rubyでプロセスを操作しています。

原著の出版は2011年と少し古いですが、主題がそうそう古びるものではないので、出版年についてはほとんど問題にならないと思います。

具体的には次のようなことがらが説明されています。

  • プロセス自体の特徴
  • forkと子プロセスとの協調
  • シグナル
  • IPC(パイプ、Unixドメインソケット)
  • 外部コマンドを実行するプロセスの生成
  • 付録として実例の説明
    • Resque, Unicorn, Spyglass(この本オリジナルのpreforkサーバ)

なぜ読んだのか

最近はわりとアプリケーションレベルの知識に偏ってインプットしていたので、日常的にあまり意識しないOSレベルの知識についてインプットしたかったというのがあります。Rubyをふだん書いている身として、RubyでUnixのことについて説明しているところに興味を持ちました。また、ちょうど達人出版会でセール中だったからというのも理由のひとつです。

実際に読んでみると、1章1章がちょうどいい詳細度の説明になっていることと、自然な日本語に訳されていることから、たいへん読みやすかったです。

どうだったか

読んでみて興味深かった点をいくつか挙げます。

子プロセスの待機

Rubyでは fork(2) を呼び出す Kernel.#fork を使うと、子プロセスを作ることができます。そして、作った子プロセスが処理を終え、終了コードなどを返すのを待つには、waitpid(2) を呼び出すRubyの Process モジュールのAPIを使うことができます。しかも、子プロセスを待つシステムコールとしての waitpid(2) に対して、Ruby側のAPIとしては次のバリエーションがあるのがおもしろいところです。

このあたりは、ライブラリ利用時にできるだけ意図が明確になるようにする、という設計思想を感じます。

ゾンビプロセス

fork(2) を使って子プロセスを作ったあと、Unixカーネルは終了した子プロセスの情報をキューに入れて管理します。Rubyでは、この子プロセスの情報は Process.#wait またはそれに類するメソッドによって取り出すことができます。この情報が取り出されずに親プロセスが終了したり、親プロセスの処理がなかなか終わらないとき、キューに子プロセスの情報が残ったままになります。この場合、この子プロセスはゾンビプロセスと呼ばれます。これまでゾンビプロセスは「pstop を叩いたときに表示されるなにか」という意識しかなかったのですが、ここの説明で「なるほど」という気持ちになりました。

ゾンビプロセスはカーネルリソースの無駄使いとなるので、子プロセスを待たなくてよい場合、Rubyでは Process.#detach でスレッドを生成して、そちらのスレッドに待たせるのがよしとされているようです。

デーモンプロセス

デーモンといえば、バックグラウンドで立ち上がっているサーバの状態のことをそう呼んでいるという認識でした。たとえば、Rackサーバを次のように起動すると、デーモンプロセスとなり、起動後に制御が端末に戻ってきます。

$ rackup -D config.ru

プロセスをデーモンプロセスと呼ばれる端末から切り離されバックグラウンドで動くものにするには、次の手順を踏む必要があります。

  • forkしたあと、親プロセスで exit することで、子プロセスをわざと孤児にする
    • 親プロセスを終了させて、制御を端末に戻す
  • setsid(2) でその子プロセスをプロセスグループとセッショングループのリーダーにする
    • これをやらないと、端末などからSIGINTなどのシグナルが送られるとき、その子プロセスが終了してしまう
  • ふたたびforkして、親プロセスで exit することで、子プロセスをわざと孤児にする
    • この時点で、新たに作った子プロセスはセッションリーダーではなく、制御端末からも完全に切り離されている
  • カレントディレクトリが削除される場合の対策として、ルートディレクトリに移る
  • 標準ストリームをすべて /dev/null にする
    • 端末につながってないので持っていてもしかたない

また、Rubyでは Process.#daemon というメソッド一発でプロセスをデーモンにすることができます。そして、 Process.#daemon の中 ではCで上の手順をまさに実行しています。

デーモンプロセス化はもっとカーネルの機能一発で実現されているかのようなイメージを抱いていたので、このような細かい手順を踏んで実行することで成り立っているというのが、個人的には意外でおもしろかったです。

おわりに

プロセスというUnixにおいて動作するプログラムを抽象化した存在について知っておくことは、Rubyを書くこと以外の場面でも活きてくると思いますし、単純に世界が広がっておもしろいので、Rubyが読める人はこの本も読んでおくとよさそうだと思いました。サンプルコードで使われるライブラリはすべてRubyの組み込みライブラリか標準添付ライブラリの範疇に収まっているので、気軽に実行できます。実際に手を動かして読むと「なるほど」感が深まると思います。

また、preforkサーバの見本として付録についているSpyglassのコードを読んでみたところ、この本で出てきた知識をフル活用していて、かつ見本ということでシンプルなコードになっているので、やってることがわかるという感覚が得られたのがよかったです。コードはほとんどRubyで書かれています(HTTPパーサなど一部がC)。このサーバのコードをひととおり把握することで、Unicornなど本番で使われるWebサーバの内部の話題にもついていきやすくなりそうだと思いました。

原著者Storimer氏の続編として、TCPソケット編とRubyスレッド編があるようなので、日本語版はないものの、こちらにも手を出したいところです。

Books by Jesse Storimer | Short, focused technical books about system programming specifically for Ruby and Rails developers.

GraphQLナイトに参加した & 『GraphQLとスキーマファースト開発』についてLTした

connpass.com

五反田のfreeeさんでGraphQLナイトが開催されたので参加してきました。会社のSlackの開発者チャンネルで@hsbtさんが貼ったリンクを見て開催に気づけたので、ありがたい限りでした。

また、LT枠が急遽空いたので、前日に飛び入りでLTすることに決めました。LT資料はこちらです。

内容としては、これまでの記事から、あまり他の人と被りなくRubyに特化しすぎることもない次のネタをあらためて紹介した感じです。

blog.kymmt.com

どういう反応になりそうかあんまりわからなかったのですが、ある程度こういう話に興味がある人は多いのかな?という感想でした。

あと、いい機会なのでサンプルを作りました。

github.com

発表はScalaのSangriaが強そうだったり、AWS AppSyncが便利そうなことにあらためて気づけたりというのがよかったです。

また、懇親会では

  • 既存のREST APIなどをGraphQLで書き直すよりは新規開発でGraphQLを選ぶ、ということが多いしやりやすい
  • やっぱり本番運用しているところは多くない
  • スキーマどうやって決めているか → フロントが欲しいJSONに合わせる、など
  • ここ最近はRelayよりApolloの事例をよく聞くし、ドキュメントも充実していてよい
  • パフォーマンスが気になる → Apollo Engineで監視とかするのが便利そう
  • graphql-rubyを使っていると、やることが少なくなるのでRailsじゃなくてもいいのではという気持ちになる

などのお話を共有できました。イベント全体で見ると、言語ではScala/Node/Ruby, 技術領域ではフロントエンド/バックエンド/モバイルアプリのようにさまざまな界隈の人が混じっていたのが、ふだんRuby系のコミュニティに顔を出すことが多い人間としては新鮮でよかったです。

主催者の@htomineさんほか運営の方々、参加者の皆さん、ありがとうございました。

graphql-guardとポリシーオブジェクトをgraphql-ruby 1.8で使うための方法

これの続きです。

blog.kymmt.com

問題

graphql-guardはインラインでポリシーを書く方法の他に、ポリシーオブジェクトを定義して GraphQL::Guard.new に渡すという方法があります。使いかたはREADMEに書いてあるとおりです。

class GraphqlPolicy
  RULES = {
    Types::QueryType => {
      viewer: ->(obj, args, ctx) { !ctx[:current_user].nil? }
    }
  }

  def self.guard(type, field)
    type.introspection? ? ->(_, _, _) { true } : RULES.dig(type, field)
  end
end

MySchema = GraphQL::Schema.define do
  query Types::QueryType

  use GraphQL::Guard.new(policy_object: GraphqlPolicy)
end

Types::QueryType = GraphQL::ObjectType.define do
  name "Query"

  field :viewer, !Types::UserType do
    resolver ->(obj, args, ctx) { ctx[:current_user] }
  end
end

# 認証に失敗すると GraphQL::Guard::NotAuthorizedError がraiseされる

で、graphql-ruby 1.8でclass-based APIを使ってスキーマを書き直すと、残念ながらこのポリシーオブジェクトは動かなくなります。

class GraphqlPolicy
  RULES = {
    Types::Query => {
      viewer: ->(obj, args, ctx) { !ctx[:current_user].nil? }
    }
  }

  def self.guard(type, field)
    type.introspection? ? ->(_, _, _) { true } : RULES.dig(type.name, field)
  end
end

class MySchema < GraphQL::Schema
  query Types::Query

  use GraphQL::Guard.new(policy_object: GraphqlPolicy)
end

class Types::Query < Types::BaseObject
  field :viewer, Types::User, null: false

  def viewer
    context[:current_user]
  end
end

# 認証に失敗すると GraphQL::Guard::NotAuthorizedError がraiseされず、
# viewerのresolverの結果がnilになるので、次のエラーになってしまう
# {
#   "data": null,
#   "errors": [
#     {
#       "message": "Cannot return null for non-nullable field Query.viewer"
#     }
#   ]
# }

解決策

上の問題の原因はHash GraphqlPolicy::RULE のキーの等価比較にあります。

graphql-guardではフィールドのinstrumentationを使っており、GraphqlPolicy.guard に渡る type は、graphql-guardの差し込むinstrumentationで取得した、class-based API以前のオブジェクト型を表す GraphQL::ObjectType のインスタンスです。一方、RULE のキーはclass-based APIで導入された GraphQL::Schema::Object のインスタンスなので、クラスがそもそも一致していません。なので、これを GraphQL::ObjectType のような古いほうのクラスのインスタンスに変換するためのメソッド #to_graphql を呼ぶ必要があります。

また、GraphQL::ObjectType では eql? をオーバーライドしていないので、Hashのキーの比較にはオブジェクトIDが使われます。ここで、#to_graphql は内部で GraphQL::ObjectType.new しているので、これで作ったインスタンスと RULE のキーになっているインスタンスはオブジェクトIDが異なってしまいます。よって、キーが一致せず、RULE からguard Procを見つけられないので、望むような動作をしなくなっています。

GraphqlPolicy を次のように定義し直せば動きます。GraphQLの型をキーとするのは諦めて、その型の名前 name をキーとすることで、Hashのキー探索がうまくハマるようにしています。

class GraphqlPolicy
  RULES = {
    Types::Query.to_graphql.name => {
      viewer: ->(obj, args, ctx) { !ctx[:current_user].nil? }
    }
  }

  def self.guard(type, field)
    type.introspection? ? ->(_, _, _) { true } : RULES.dig(type.name, field)
  end
end

# 認証に失敗すると GraphQL::Guard::NotAuthorizedError がraiseされる

雑感

この件はgemのREADMEを改善したほうがよさそう。また、なにかもうちょっといい方法があれば教えてください。

graphql-guardをgraphql-ruby 1.8で使うためのある方法

問題

graphql-ruby 1.8ではclass-based APIが導入されました。フィールド定義は型を表すクラスのコンテキストでクラスメソッド field を呼ぶ形で定義します。

class Types::Query < Types::BaseObject
  field :viewer, Types::User, null: true

  def viewer
    context[:current_user]
  end
end

このときgraphql-guard(今回はv1.1.0)を次のように使おうとすると #<ArgumentError: unknown keyword: guard> になります。

class Types::Query < Types::BaseObject
  field :viewer, Types::User, guard ->(obj, args, ctx) { !ctx[:current_user].nil? }, null: true

  def viewer
    context[:current_user]
  end
end

class-based APIでフィールドを表すクラスが GraphQL::Field から GraphQL::Schema::Field にかわり、従来のように guard をキーとするProcを指定するだけでは、フィールドのメタデータに guard を設定することができなくなったためエラーになっています。

ある解決策

フィールドをカスタマイズして guard をメタデータとして持たせられるようにします。やりかたはこのドキュメントを参照。

GraphQL - Extending the GraphQL-Ruby Type Definition System

class Fields::Guardable < GraphQL::Schema::Field
  def initialize(*args, guard: nil, **kwargs, &block)
    @guard = guard
    super *args, **kwargs, &block
  end

  def to_graphql
    field_defn = super
    field_defn.tap { |d| d.metadata[:guard] = @guard }
  end
end

initialize はこのフィールドを定義するときに guard を設定できるようにしています。また、スキーマ定義が処理される過程で to_graphql が実行されるので、このときにフィールド定義を表す GraphQL::Schema のインスタンスが持つ属性 metadataguard を設定しています。

これで、class-based APIでもフィールド定義時に guard Procが設定できます。guard を定義できるフィールドにするために field_class を明示的に指定します。

class Types::Query < Types::BaseObject
  field_class Fields::Guardable

  field :viewer, Types::User, guard ->(obj, args, ctx) { !ctx[:current_user].nil? }, null: true

  def viewer
    context[:current_user]
  end
end

accepts_definitionを使う[2018-06-10 追記]

accepts_definition を利用すると1.8以前で使っていたオプションをclass-based APIで使うときにgraphql-ruby側でいい感じにハンドリングしてくれます。

GraphQL - Extending the GraphQL-Ruby Type Definition System

なので、Fields::Guardable に対して次のようにすればOKでした。

class Fields::Guardable < GraphQL::Schema::Field
  accepts_definition :guard
end

これを使えば従来どおり guard が使えます。

class Types::Query < Types::BaseObject
  field_class Fields::Guardable

  field :viewer, Types::User, null: true do
    guard ->(obj, args, ctx) { !ctx[:current_user].nil? }
  end

  def viewer
    context[:current_user]
  end
end

内部的には前節の説明に近いことをやっているようです。

移行時に補助的に使うメソッドのようにも見えるので、今後使い続けていいのかがちょっと微妙なところです。

雑感

  • Policyオブジェクトを使うときにどうやるか
  • Fields::Guardable のようなモジュールをgem側で提供できるようにしたほうがよさそうかも