Class: Rails::GraphQL::Request::Strategy
- Inherits:
-
Object
- Object
- Rails::GraphQL::Request::Strategy
- Extended by:
- ActiveSupport::Autoload
- Defined in:
- lib/rails/graphql/request/strategy.rb
Overview
GraphQL Request Strategy
This is the base class for the strategies of resolving a request.
Direct Known Subclasses
Defined Under Namespace
Classes: CachedStrategy, DynamicInstance, MultiQueryStrategy, SequencedStrategy
Constant Summary collapse
- PREPARE_XARGS =
Configurations for the prepare step
{ object?: true, reverse?: true }.freeze
Instance Attribute Summary collapse
-
#context ⇒ Object
readonly
Returns the value of attribute context.
-
#listeners ⇒ Object
readonly
Returns the value of attribute listeners.
-
#request ⇒ Object
readonly
Returns the value of attribute request.
-
#stage ⇒ Object
readonly
Returns the value of attribute stage.
Class Method Summary collapse
-
.can_resolve?(_) ⇒ Boolean
Check if the strategy can resolve the given
request.
Instance Method Summary collapse
-
#add_listeners_from(object) ⇒ Object
Check what kind of event listeners the object have, in order to speed up processing by avoiding unnecessary event instances.
-
#cache_dump ⇒ Object
Build the cache object.
-
#cache_load(data) ⇒ Object
Organize from cache data.
-
#clear ⇒ Object
Clear all strategy information.
-
#compile ⇒ Object
Simply run the organize step for compilation.
-
#find_directive!(directive) ⇒ Object
Find a given
directiveand store it on request cache. -
#find_type!(type) ⇒ Object
Find a given
typeand store it on request cache. -
#initialize(request) ⇒ Strategy
constructor
A new instance of Strategy.
-
#instance_for(klass) ⇒ Object
Check if the given class is in the pool, or add a new instance to the pool, and then set the instance as the current object.
-
#listeners? ⇒ Boolean
Check if any listener were actually added.
-
#listening_to?(event_name) ⇒ Boolean
Check if any object is listening to a given
event_name. -
#perform(field, data = nil) ⇒ Object
When a
fieldhas a perform step, run it under the context of the prepared value from the data pool. -
#prepare(field, &block) ⇒ Object
Execute the prepare step for the given
fieldand execute the given block using context stack. -
#prepared_data_for(field, with_null: false) ⇒ Object
Get the prepared data for the given
field, getting ready for resolve, while ensuring to check prepared data on request. -
#resolve(field, *args, array: false, decorate: false, &block) ⇒ Object
Resolve a value for a given object, It uses the
argsto prevent problems with nil values. -
#resolve! ⇒ Object
Executes the strategy in the normal mode.
-
#resolve_data_for(field, args) ⇒ Object
Get the resolved data for a given field.
-
#safe_store_data(field, value = nil) ⇒ Object
Only store a given
valuefor a givenfieldif it is not set yet. -
#stacked(object, &block) ⇒ Object
When running an stacked operation, make sure that the object was added to the list of the listeners.
-
#store_data(field, value) ⇒ Object
Store a given resolve
valuefor a givenfield. -
#trigger_event(event_name, **xargs) ⇒ Object
Trigger an event using a set of filtered objects from
request.stack.
Constructor Details
#initialize(request) ⇒ Strategy
Returns a new instance of Strategy.
37 38 39 40 41 42 |
# File 'lib/rails/graphql/request/strategy.rb', line 37 def initialize(request) @request = request @objects_pool = {} @listeners = Hash.new { |h, k| h[k] = Set.new } add_listeners_from(request) end |
Instance Attribute Details
#context ⇒ Object (readonly)
Returns the value of attribute context.
26 27 28 |
# File 'lib/rails/graphql/request/strategy.rb', line 26 def context @context end |
#listeners ⇒ Object (readonly)
Returns the value of attribute listeners.
26 27 28 |
# File 'lib/rails/graphql/request/strategy.rb', line 26 def listeners @listeners end |
#request ⇒ Object (readonly)
Returns the value of attribute request.
26 27 28 |
# File 'lib/rails/graphql/request/strategy.rb', line 26 def request @request end |
#stage ⇒ Object (readonly)
Returns the value of attribute stage.
26 27 28 |
# File 'lib/rails/graphql/request/strategy.rb', line 26 def stage @stage end |
Class Method Details
.can_resolve?(_) ⇒ Boolean
Check if the strategy can resolve the given request. By default, strategies cannot resolve a request. Override this method with a valid checker.
32 33 34 |
# File 'lib/rails/graphql/request/strategy.rb', line 32 def can_resolve?(_) false end |
Instance Method Details
#add_listeners_from(object) ⇒ Object
Check what kind of event listeners the object have, in order to speed up processing by avoiding unnecessary event instances
187 188 189 190 191 192 193 194 195 |
# File 'lib/rails/graphql/request/strategy.rb', line 187 def add_listeners_from(object) object.all_listeners&.each do |event_name| listeners[event_name] << object end if request.prepared_data_for?(object) listeners[:prepare] << object end end |
#cache_dump ⇒ Object
Build the cache object
226 227 228 |
# File 'lib/rails/graphql/request/strategy.rb', line 226 def cache_dump { class: self.class } end |
#cache_load(data) ⇒ Object
Organize from cache data
231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 |
# File 'lib/rails/graphql/request/strategy.rb', line 231 def cache_load(data) data, operations, fragments = data.values_at(:strategy, :operations, :fragments) collect_listeners do # Load all operations operations = operations.transform_values do |operation| request.build_from_cache(operation.delete(:type)).tap do |instance| instance.instance_variable_set(:@request, request) instance.cache_load(operation) end end # Load all fragments fragments = fragments&.transform_values do |fragment| request.build_from_cache(Component::Fragment).tap do |instance| instance.instance_variable_set(:@request, request) instance.cache_load(fragment) end end end # Mark itself as already organized @organized = true # Save operations and fragments into the request request.instance_variable_set(:@operations, operations) request.instance_variable_set(:@fragments, fragments) end |
#clear ⇒ Object
Clear all strategy information
45 46 47 48 49 |
# File 'lib/rails/graphql/request/strategy.rb', line 45 def clear @listeners.clear @objects_pool.clear @stage = @context = @objects_pool = @data_pool = @listeners = nil end |
#compile ⇒ Object
Simply run the organize step for compilation
221 222 223 |
# File 'lib/rails/graphql/request/strategy.rb', line 221 def compile for_each_operation { |op| collect_listeners { op.organize! } } end |
#find_directive!(directive) ⇒ Object
Find a given directive and store it on request cache
62 63 64 |
# File 'lib/rails/graphql/request/strategy.rb', line 62 def find_directive!(directive) request.nested_cache(:directives, directive) { schema.find_directive!(directive) } end |
#find_type!(type) ⇒ Object
Find a given type and store it on request cache
57 58 59 |
# File 'lib/rails/graphql/request/strategy.rb', line 57 def find_type!(type) request.nested_cache(:types, type) { schema.find_type!(type) } end |
#instance_for(klass) ⇒ Object
Check if the given class is in the pool, or add a new instance to the pool, and then set the instance as the current object
146 147 148 149 150 151 152 153 154 155 156 157 |
# File 'lib/rails/graphql/request/strategy.rb', line 146 def instance_for(klass) @objects_pool[klass] ||= begin @objects_pool.each_value.find do |value| value.is_a?(klass) end || begin instance = klass.new instance = DynamicInstance.new(instance) unless klass < GraphQL::Schema || klass < GraphQL::Type::Object instance end end end |
#listeners? ⇒ Boolean
Check if any listener were actually added
67 68 69 |
# File 'lib/rails/graphql/request/strategy.rb', line 67 def listeners? listeners.any? end |
#listening_to?(event_name) ⇒ Boolean
Check if any object is listening to a given event_name
72 73 74 |
# File 'lib/rails/graphql/request/strategy.rb', line 72 def listening_to?(event_name) listeners? && listeners.key?(event_name.to_sym) end |
#perform(field, data = nil) ⇒ Object
When a field has a perform step, run it under the context of the prepared value from the data pool
84 85 86 87 88 89 90 |
# File 'lib/rails/graphql/request/strategy.rb', line 84 def perform(field, data = nil) context.stacked(data || @data_pool[field]) do safe_store_data(field) do Event.trigger(:perform, field, self, &field.performer) end end end |
#prepare(field, &block) ⇒ Object
Execute the prepare step for the given field and execute the given block using context stack
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
# File 'lib/rails/graphql/request/strategy.rb', line 94 def prepare(field, &block) check_fragment_multiple_prepare!(field) value = safe_store_data(field) do prepared = request.prepared_data_for(field) if prepared.is_a?(PreparedData) field.prepared_data! prepared.all else Event.trigger(:prepare, field, self, **PREPARE_XARGS) end end perform(field, value) if field.mutation? value = @data_pool[field] context.stacked(value, &block) unless value.nil? end |
#prepared_data_for(field, with_null: false) ⇒ Object
Get the prepared data for the given field, getting ready for resolve, while ensuring to check prepared data on request
210 211 212 213 214 215 216 217 218 |
# File 'lib/rails/graphql/request/strategy.rb', line 210 def prepared_data_for(field, with_null: false) if field.prepared_data? request.prepared_data_for(field).next elsif @data_pool.key?(field) @data_pool[field] elsif with_null PreparedData::NULL end end |
#resolve(field, *args, array: false, decorate: false, &block) ⇒ Object
Resolve a value for a given object, It uses the args to prevent problems with nil values.
114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
# File 'lib/rails/graphql/request/strategy.rb', line 114 def resolve(field, *args, array: false, decorate: false, &block) resolve_data_for(field, args) value = args.last value = field.decorate(value) if decorate context.stacked(value) do |current| if !array block.call(current) field.write_value(current) else field.write_array(current, &block) end end end |
#resolve! ⇒ Object
Executes the strategy in the normal mode
52 53 54 |
# File 'lib/rails/graphql/request/strategy.rb', line 52 def resolve! raise NotImplementedError end |
#resolve_data_for(field, args) ⇒ Object
Get the resolved data for a given field
130 131 132 133 134 135 136 137 138 139 140 141 142 |
# File 'lib/rails/graphql/request/strategy.rb', line 130 def resolve_data_for(field, args) return unless args.size.zero? if field.try(:dynamic_resolver?) extra = prepared_data_for(field, with_null: true) extra = extra === PreparedData::NULL ? EMPTY_HASH : { prepared: extra } args << Event.trigger(:resolve, field, self, **extra, &field.resolver) elsif field.prepared_data? args << prepared_data_for(field) else data_for(args, field) end end |
#safe_store_data(field, value = nil) ⇒ Object
Only store a given value for a given field if it is not set yet
203 204 205 206 |
# File 'lib/rails/graphql/request/strategy.rb', line 203 def safe_store_data(field, value = nil) value ||= yield if block_given? @data_pool[field] ||= value unless value.nil? end |
#stacked(object, &block) ⇒ Object
When running an stacked operation, make sure that the object was added to the list of the listeners
78 79 80 |
# File 'lib/rails/graphql/request/strategy.rb', line 78 def stacked(object, &block) request.stacked(object, &block) end |
#store_data(field, value) ⇒ Object
Store a given resolve value for a given field
198 199 200 |
# File 'lib/rails/graphql/request/strategy.rb', line 198 def store_data(field, value) @data_pool[field] = value end |
#trigger_event(event_name, **xargs) ⇒ Object
Trigger an event using a set of filtered objects from request.stack. trigger_all. The filter is based on the listeners that were collected by the strategy.
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 |
# File 'lib/rails/graphql/request/strategy.rb', line 163 def trigger_event(event_name, **xargs) return unless listening_to?(event_name) # A simpler attempt to remove select less objects (or even none) by # assuming that the first item will work as exclusive and # non-exclusive, and the others, only non-exclusive or anything # different than a +Callback+ event_name = event_name.to_sym list = listeners[event_name] objects = request.stack.select.with_index do |obj, idx| next unless list.include?(obj) next true if idx == 0 obj.all_events.try(:[], event_name)&.any? do |ev| !(ev.is_a?(Callback) && ev.exclusive?) end end # Now trigger with more for all the selected objects Event.trigger(event_name, objects, self, **xargs) if objects.present? end |