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