Skip to content

Instantly share code, notes, and snippets.

@midhunkrishna
Last active July 16, 2020 12:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save midhunkrishna/bdea43daff6f588757a21beec491627e to your computer and use it in GitHub Desktop.
Save midhunkrishna/bdea43daff6f588757a21beec491627e to your computer and use it in GitHub Desktop.

graphql ruby dynamically refresh schema, well a portion of it. ;)

class Types::DrugType
  
  # hook method that is called to check whether fields changed or not. 
  def self.dynamic_field_hook
    # this is example code, basically if the dynamic fields 
    # defined for a `SomeType` changes, the checksum changes as well
    DynamicField.for(DrugType).checksum
  end
  
  # arg1: hook method name
  # arg2: block which is evaluated when hook value changes from previous stored value
  
  dynamic_fields('dynamic_field_hook') do
    DynamicField.for(DrugType).map do |field|
      {    
        name: field.name,
        type: field.type,
        description: field.description,
        options: field.options,
        dfn: -> { object.dynamic_fields[field.name] },
      }
    end
  end
end
module Schema
  module Member
    module HasDynamicFields
      def dynamic_fields(hook, &block)
        @dynamic_block = block
        @refresh_hook = hook
        generate_and_register_fields
      end

      def fields
        conditional_refresh
        super
      end

      def get_field(field_name)
        conditional_refresh
        super
      end

      def own_fields
        conditional_refresh
        super
      end

      private

      def mutex
        @mutex ||= Mutex.new
      end

      def conditional_refresh
        return unless @refresh_hook

        hook_value = send(@refresh_hook)

        if @hook_value != hook_value
          @hook_value = hook_value
          refresh
        end
      end

      def refresh
        mutex.synchronize do
          (@dynamic_fields || {}).each do |key, value|
            @own_fields.delete(key)
          end
          generate_and_register_fields
        end
      end

      def generate_and_register_fields
        fields = @dynamic_block.call.map do |field_opts|
          proc = field_opts.delete(:dfn)
          define_method(field_opts[:name], proc)

          field_class.from_options(
            field_opts[:name],
            field_opts[:type],
            field_opts[:description],
            owner: self,
            **field_opts[:options]
          )
        end

        @dynamic_fields = {}

        fields.each do |field_dfn|
          @dynamic_fields[field_dfn.name] = field_dfn
          @own_fields[field_dfn.name] = field_dfn
        end
      end
    end
  end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment