超絶怒涛のゆるふわコードリーディング GraphQL gem篇
GraphQL gemをふんわり読んだ
ふんわり読んで見ました、とりあえずよく使われてそうなDSLをおったメモ
versionはv1.9.12 が最新リリースである時期のmasterブランチ読みました。
ゆるふわの極みです
とりあえず最初に公式のGetting Startedで出てくるやつ
module Types class PostType < Types::BaseObject field :id, ID, null: false field :title, String, null: false field :truncated_preview, String, null: false field :comments, [Types::CommentType], null: true, description: "This post's comments, or null if this post has comments disabled." end end
field is 何?
GraphQL::Schema::ObjectがextendしてるGraphQL::Schema::Member::HasFieldsのメソッド。
def field(*args, **kwargs, &block) field_defn = field_class.from_options(*args, owner: self, **kwargs, &block) add_field(field_defn) nil end
field_class is 何?
def field_class(new_field_class = nil) if new_field_class @field_class = new_field_class elsif @field_class @field_class elsif self.is_a?(Class) superclass.respond_to?(:field_class) ? superclass.field_class : GraphQL::Schema::Field else ancestor = ancestors[1..-1].find { |a| a.respond_to?(:field_class) && a.field_class } ancestor ? ancestor.field_class : GraphQL::Schema::Field end end
引数はわたしてないので一個目の分岐は飛ばして、@field_classも(多分素のままだと)設定されてないのでGraphQL::Schema::Fieldか何がしかのクラスが返されているっぽい GraphQL::Schema::Field.from_optionsはオプションを色々整えた後newしてインスタンス返してくれるメソッド。
def self.from_options(name = nil, type = nil, desc = nil, resolver: nil, mutation: nil, subscription: nil,**kwargs, &block)
引数の数すごい
最終的に下のコードはGraphQL::Schema::Fieldかサブクラスかなにかのインスタンス作ってると言えそう。
field_defn = field_class.from_options(*args, owner: self, **kwargs, &block)
add_field is 何
つぎはこの行
add_field(field_defn)
def add_field(field_defn) if CONFLICT_FIELD_NAMES.include?(field_defn.original_name) && field_defn.original_name == field_defn.resolver_method warn "#{self.graphql_name}'s `field :#{field_defn.original_name}` conflicts with a built-in method, use `resolver_method:` to pick a different resolver method for this field (for example, `resolver_method: :resolve_#{field_defn.original_name}` and `def resolve_#{field_defn.original_name}`)" end own_fields[field_defn.name] = field_defn nil end
:context, :object, :method, :class 書くと警告されるっぽい。 中身はハッシュで名前をキーにしてfield_class.from_optionsでつくったインスタンスをそのまま突っ込んでる。
んでこのハッシュは下記の2つのメソッドで使ってるっぽい
def fields # Local overrides take precedence over inherited fields all_fields = {} ancestors.reverse_each do |ancestor| if ancestor.respond_to?(:own_fields) all_fields.merge!(ancestor.own_fields) end end all_fields end def get_field(field_name) if (f = own_fields[field_name]) f else for ancestor in ancestors if ancestor.respond_to?(:own_fields) && f = ancestor.own_fields[field_name] return f end end nil end end
多分実行してるときに使ってるコード
public_sendしてますね。引数も渡せそう。
オブジェクトがもともとメソッドもってればなんでもフィールドにできる感じなのかな?
def resolve_field_method(obj, ruby_kwargs, ctx) if obj.object.is_a?(Hash) inner_object = obj.object if inner_object.key?(@method_sym) inner_object[@method_sym] else inner_object[@method_str] end elsif obj.object.respond_to?(@method_sym) if ruby_kwargs.any? obj.object.public_send(@method_sym, **ruby_kwargs) else obj.object.public_send(@method_sym) end else raise <<-ERR Failed to implement #{@owner.graphql_name}.#{@name}, tried: - `#{obj.class}##{@resolver_method}`, which did not exist - `#{obj.object.class}##{@method_sym}`, which did not exist - Looking up hash key `#{@method_sym.inspect}` or `#{@method_str.inspect}` on `#{obj.object}`, but it wasn't a Hash To implement this field, define one of the methods above (and check for typos) ERR end end
前半の分岐がよくわからんけど
@method_symはこんな感じでinitializeされるときに入れてる
method_name = method || hash_key || @underscored_name @method_sym = method_name.to_sym
まとめ
ちょっと読んだ後公式のドキュメントみて使い方知らないとわけわからんことに気づいたのでここで終わりです。
OSSに貢献してる人これ無償で読んで理解して書いてるってすごすぎませんか??????