問題
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側で提供できるようにしたほうがよさそうかも