lib/rails/graphql/source/scoped_arguments.rb in rails-graphql-0.2.1 vs lib/rails/graphql/source/scoped_arguments.rb in rails-graphql-1.0.0.beta

- old
+ new

@@ -1,57 +1,65 @@ # frozen_string_literal: true -module Rails # :nodoc: - module GraphQL # :nodoc: +module Rails + module GraphQL # This is a helper class that allows sources to have scoped-based arguments, # meaning that when an argument is present, it triggers the underlying block # on the fields where the argument was attached to - # - # TODO: Easy the usage of scoped arguments with AR, to map model scopes as - # arguments using the abilities here provided module Source::ScopedArguments def self.included(other) other.extend(ClassMethods) end # Extended argument class to be the instance of the scoped class Argument < GraphQL::Argument - attr_reader :block, :fields - - def initialize(*args, on: nil, **xargs, &block) + def initialize(*args, block:, on: nil, **xargs) super(*args, **xargs) @block = block - @fields = Array.wrap(on).presence + @fields = GraphQL.enumerate(on) end + # Apply the argument block to the given object, using or not the value + def apply_to(object, value) + callable = @block.is_a?(Symbol) ? object.method(@block) : @block + raise ::ArgumentError, (+<<~MSG) unless callable.respond_to?(:call) + Unable to call "#{@block.inspect}" on #{object.class}. + MSG + + args = (callable.arity == 1 || callable.arity == -1) ? [value] : nil + + return callable.call(*args) if callable.is_a?(Method) + object.instance_exec(*args, &callable) + end + # Check if the argument should be attached to the given +field+ def attach_to?(field) - return true if fields.nil? + return true if @fields.nil? - fields.any? do |item| + @fields.any? do |item| (item.is_a?(Symbol) && field.name.eql?(item)) || field.gql_name.eql?(item) end end end - module ClassMethods # :nodoc: + module ClassMethods # Return the list of scoped params defined def scoped_arguments defined?(@scoped_arguments) ? @scoped_arguments : {} end protected # Add a new scoped param to the list def scoped_argument(param, type = :string, proc_method = nil, **settings, &block) block = proc_method if proc_method.present? && block.nil? - argument = Argument.new(param, type, **settings, owner: self, &block) + argument = Argument.new(param, type, **settings, owner: self, block: block) (@scoped_arguments ||= {})[argument.name] = argument end - alias scoped_arg scoped_arguments + alias scoped_arg scoped_argument # Helper method to attach the scoped arguments to a given +field+ def attach_scoped_arguments_to(*fields, safe: true) fields = fields.flatten.compact return if fields.blank? @@ -67,17 +75,21 @@ end # Find all the executable arguments attached to the running field and # call them with the given object def inject_scopes(object, assigned_to = nil) - return object if event.field.nil? || (args_source = event.send(:args_source)).nil? + return object if event.field.nil? || (field_args = event.field.all_arguments).blank? + args_source = event.send(:args_source) event.data[assigned_to] ||= object unless assigned_to.nil? - event.field.all_arguments.each_value.inject(object) do |result, argument| - next result unless argument.respond_to?(:block) && args_source.key?(argument.name) - send_args = argument.block.arity.eql?(1) ? [args_source[argument.name]] : [] + field_args.each_value.inject(object) do |result, argument| + arg_value = args_source.key?(argument.name) \ + ? args_source[argument.name] \ + : argument.default - value = result.instance_exec(*send_args, &argument.block) + next result if arg_value.nil? || !argument.is_a?(Argument) + + value = argument.apply_to(result, arg_value) value = value.nil? ? result : value assigned_to.nil? ? value : event.data[assigned_to] = value end end