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] endendこのとき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] endendclass-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 } endendinitialize はこのフィールドを定義するときに 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] endendaccepts_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 :guardendこれを使えば従来どおり 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] endend移行時に補助的に使うメソッドのようにも見えるので、今後使い続けていいのかがちょっと微妙なところです。
雑感
- Policyオブジェクトを使うときにどうやるか
Fields::Guardableのようなモジュールをgem側で提供できるようにしたほうがよさそうかも