@@ -59,6 +59,14 @@ def perform(elements)
5959 elements . each { |element | fulfill ( element , [ element , something ] ) }
6060 end
6161 end
62+
63+ n1_optimized :extra_data do
64+ def perform ( elements )
65+ Entity . perform!
66+
67+ elements . each { |element | fulfill ( element , [ element , :extra ] ) }
68+ end
69+ end
6270 end )
6371
6472 stub_const ( "Company" , Class . new ( ActiveRecord ::Base ) do
@@ -311,6 +319,61 @@ def perform(elements)
311319 end
312320 end
313321
322+ context "with context binding" do
323+ let ( :objects ) { [ Entity . create! , Entity . create! ] }
324+
325+ describe "n1_bind_to" do
326+ it "returns correct data for each bound object" do
327+ objects . each { |obj | obj . n1_bind_to ( objects ) }
328+
329+ expect ( objects . first . data ) . to eq ( [ objects . first ] )
330+ expect ( objects . last . data ) . to eq ( [ objects . last ] )
331+ end
332+
333+ it "loads all bound objects in a single batch" do
334+ objects . each { |obj | obj . n1_bind_to ( objects ) }
335+
336+ expect { objects . map ( &:data ) } . to change ( Entity , :count ) . by ( 1 )
337+ end
338+
339+ it "caches the result after the first load" do
340+ objects . each { |obj | obj . n1_bind_to ( objects ) }
341+
342+ expect { objects . map ( &:data ) } . to change ( Entity , :count ) . by ( 1 )
343+ expect { objects . map ( &:data ) } . not_to change ( Entity , :count )
344+ end
345+
346+ it "lazily loads when the first object is accessed" do
347+ objects . each { |obj | obj . n1_bind_to ( objects ) }
348+
349+ expect { objects . first . data } . to change ( Entity , :count ) . by ( 1 )
350+ expect { objects . last . data } . not_to change ( Entity , :count )
351+ end
352+ end
353+
354+ describe "automatic context binding" do
355+ it "automatically binds objects after loading via preloader" do
356+ N1Loader ::Preloader . new ( objects ) . preload ( :data )
357+
358+ # Accessing data triggers perform for all objects and auto-calls n1_bind_to
359+ objects . first . data
360+
361+ # extra_data should now batch in a single perform call for all bound objects
362+ expect { objects . map ( &:extra_data ) } . to change ( Entity , :count ) . by ( 1 )
363+ end
364+
365+ it "does not trigger another load for siblings once auto-bound" do
366+ N1Loader ::Preloader . new ( objects ) . preload ( :data )
367+
368+ # First element access triggers auto-bind for the entire collection
369+ objects . first . data
370+
371+ objects . first . extra_data
372+ expect { objects . last . extra_data } . not_to change ( Entity , :count )
373+ end
374+ end
375+ end
376+
314377 context "with deep includes" do
315378 let ( :objects ) { Company . includes ( data : :company ) }
316379
0 commit comments