Class: Graphiti::Scope

Inherits:
Object show all
Defined in:
lib/graphiti/scope.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(object, resource, query, opts = {}) ⇒ Scope

Returns a new instance of Scope.



22
23
24
25
26
27
28
29
30
31
# File 'lib/graphiti/scope.rb', line 22

def initialize(object, resource, query, opts = {})
  @object = object
  @resource = resource
  @query = query
  @opts = opts

  @object = @resource.around_scoping(@object, @query.hash) { |scope|
    apply_scoping(scope, opts)
  }
end

Instance Attribute Details

#objectObject

Returns the value of attribute object.



3
4
5
# File 'lib/graphiti/scope.rb', line 3

def object
  @object
end

#paginationObject (readonly)

Returns the value of attribute pagination.



4
5
6
# File 'lib/graphiti/scope.rb', line 4

def pagination
  @pagination
end

#unpaginated_objectObject

Returns the value of attribute unpaginated_object.



3
4
5
# File 'lib/graphiti/scope.rb', line 3

def unpaginated_object
  @unpaginated_object
end

Class Method Details

.thread_pool_executorObject



8
9
10
11
12
13
14
15
16
17
18
19
20
# File 'lib/graphiti/scope.rb', line 8

def self.thread_pool_executor
  return @thread_pool_executor if @thread_pool_executor

  concurrency = Graphiti.config.concurrency_max_threads || 4
  @thread_pool_executor ||= @thread_pool_executor_mutex.synchronize do
    Concurrent::ThreadPoolExecutor.new(
      min_threads: 0,
      max_threads: concurrency,
      max_queue: concurrency * 4,
      fallback_policy: :caller_runs
    )
  end
end

Instance Method Details

#resolveObject



33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/graphiti/scope.rb', line 33

def resolve
  if @query.zero_results?
    []
  else
    resolved = broadcast_data { |payload|
      @object = @resource.before_resolve(@object, @query)
      payload[:results] = @resource.resolve(@object)
      payload[:results]
    }
    resolved.compact!
    assign_serializer(resolved)
    yield resolved if block_given?
    @opts[:after_resolve]&.call(resolved)
    resolve_sideloads(resolved) unless @query.sideloads.empty?
    resolved
  end
end

#resolve_sideloads(results) ⇒ Object



51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/graphiti/scope.rb', line 51

def resolve_sideloads(results)
  return if results == []

  concurrent = Graphiti.config.concurrency
  promises = []

  @query.sideloads.each_pair do |name, q|
    sideload = @resource.class.sideload(name)
    next if sideload.nil? || sideload.shared_remote?
    parent_resource = @resource
    graphiti_context = Graphiti.context
    resolve_sideload = -> {
      Graphiti.config.before_sideload&.call(graphiti_context)
      Graphiti.context = graphiti_context
      sideload.resolve(results, q, parent_resource)
      @resource.adapter.close if concurrent
    }
    if concurrent
      promises << Concurrent::Promise.execute(executor: self.class.thread_pool_executor, &resolve_sideload)
    else
      resolve_sideload.call
    end
  end

  if concurrent
    # Wait for all promises to finish
    sleep 0.01 until promises.all? { |p| p.fulfilled? || p.rejected? }
    # Re-raise the error with correct stacktrace
    # OPTION** to avoid failing here?? if so need serializable patch
    # to avoid loading data when association not loaded
    if (rejected = promises.find(&:rejected?))
      raise rejected.reason
    end
  end
end