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
のインスタンスが持つ属性 metadata
に guard
を設定しています。
これで、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側で提供できるようにしたほうがよさそうかも