33module GraphQL
44 class Dataloader
55 class Loader
6+ # TODO: this is basically a reimplementation of Promise.rb
7+ # Should I just take on that dependency, or is there a value in a
8+ # custom implementation?
9+ class PendingLoad
10+ attr_writer :loaded
11+ attr_reader :pending_thens
12+
13+ def initialize ( loader , key )
14+ @loader = loader
15+ @key = key
16+ @loaded = false
17+ @pending_thens = [ ]
18+ end
19+
20+ def sync
21+ if !@loaded
22+ @loaded = true
23+ if @loader . nil?
24+ binding . pry
25+ end
26+ @loader . sync
27+ end
28+ @loader . fulfilled_value_for ( @key )
29+ end
30+
31+ def value
32+ if !@fully_loaded
33+ @fully_loaded = true
34+ v = sync
35+ if v . is_a? ( PendingLoad )
36+ v = v . value
37+ end
38+ @fully_loaded_value = v
39+ end
40+ @fully_loaded_value
41+ end
42+
43+ def then ( &next_block )
44+ pending_then = ThenBlock . new ( self , next_block )
45+ if !@loaded
46+ @pending_thens << pending_then
47+ end
48+ pending_then
49+ end
50+ end
51+
52+ class ThenBlock < PendingLoad
53+ def initialize ( pending_load , then_block )
54+ @pending_load = pending_load
55+ @then_block = then_block
56+ @loaded = false
57+ @pending_thens = [ ]
58+ @value = nil
59+ end
60+
61+ def sync
62+ if !@loaded
63+ @loaded = true
64+ @value = @then_block . call ( @pending_load . sync )
65+ @pending_thens . each ( &:sync )
66+ end
67+ @value
68+ end
69+ end
70+
71+ class AllPendingLoads < PendingLoad
72+ def initialize ( pending_loads )
73+ @loaded = false
74+ @value = nil
75+ @pending_loads = pending_loads
76+ @pending_thens = [ ]
77+ end
78+
79+ def sync
80+ if !@loaded
81+ @loaded = true
82+ @value = @pending_loads . map ( &:sync )
83+ @pending_thens . each ( &:sync )
84+ end
85+ @value
86+ end
87+ end
88+
689 def self . load ( context , *key , value )
790 self . for ( context , *key ) . load ( value )
891 end
@@ -13,7 +96,8 @@ def self.for(context, *key_parts)
1396 end
1497
1598 def self . load_all ( context , key , values )
16- GraphQL ::Execution ::Lazy . all ( values . map { |value | load ( context , key , value ) } )
99+ pending_loads = values . map { |value | load ( context , key , value ) }
100+ AllPendingLoads . new ( pending_loads )
17101 end
18102
19103 def initialize ( context , *key )
@@ -23,14 +107,8 @@ def initialize(context, *key)
23107 @loaded_values = { }
24108 end
25109
26- def load ( value )
27- @promises [ value ] ||= GraphQL ::Execution ::Lazy . new do
28- if !@loaded_values . key? ( value )
29- sync
30- end
31- # TODO raise if key is missing?
32- @loaded_values [ value ]
33- end
110+ def load ( key )
111+ @promises [ key ] ||= PendingLoad . new ( self , key )
34112 end
35113
36114 def sync
@@ -42,12 +120,20 @@ def sync
42120
43121 def fulfill ( key , value )
44122 @loaded_values [ key ] = value
123+ @promises [ key ] . loaded = true
124+ @promises [ key ] . pending_thens . each ( &:sync )
125+ value
45126 end
46127
47128 def fulfilled? ( key )
48129 @loaded_values . key? ( key )
49130 end
50131
132+ def fulfilled_value_for ( key )
133+ # TODO raise if not loaded?
134+ @loaded_values [ key ]
135+ end
136+
51137 def perform ( values )
52138 raise NotImplementedError , "`#{ self . class } #perform` should load `values` for `@key` and return an item for each of `values`"
53139 end
0 commit comments