2016-09-29 1 views
0

Ich habe mit einigen der internen Schienen-Code in Rails 4.2.7 mit Feder preloader experimentiert und einige ungerade Verhalten von Stack-Dumps in irb gefunden.Blöcke und Call-Bereich in IRB Verhalten inkonsistent

Betrachten Sie den folgenden Code

def run_block 
    yield 
end 

class Vehicle < ActiveRecord::Base 
    def self.instantiate(*args) 
    puts caller_locations 
    super 
    end 

    def self.test_stack 
    run_block {Vehicle.all} 
    end 
end 

Mitteilung, dass ich die Funktionalität der instantiate Funktion erstreckt (http://apidock.com/rails/ActiveRecord/Base/instantiate/class) aus dem Call-Stack zu drucken.

Wenn ich eine Schiene c Konsole öffnen und laufe

run_block {puts caller_locations} 

Ich erhalte den Stack-Trace

test_irb/app/models/vehicle.rb:2:in `run_block' 
(irb):4:in `irb_binding' 
.rvm/rubies/ruby-head/lib/ruby/2.4.0/irb/workspace.rb:87:in `eval' 
.rvm/rubies/ruby-head/lib/ruby/2.4.0/irb/workspace.rb:87:in `evaluate' 
.rvm/rubies/ruby-head/lib/ruby/2.4.0/irb/context.rb:380:in `evaluate' 
.rvm/rubies/ruby-head/lib/ruby/2.4.0/irb.rb:489:in `block (2 levels) in eval_input' 
.rvm/rubies/ruby-head/lib/ruby/2.4.0/irb.rb:623:in `signal_status' 
.rvm/rubies/ruby-head/lib/ruby/2.4.0/irb.rb:486:in `block in eval_input' 
.rvm/rubies/ruby-head/lib/ruby/2.4.0/irb/ruby-lex.rb:246:in `block (2 levels) in each_top_level_statement' 
.rvm/rubies/ruby-head/lib/ruby/2.4.0/irb/ruby-lex.rb:232:in `loop' 
.rvm/rubies/ruby-head/lib/ruby/2.4.0/irb/ruby-lex.rb:232:in `block in each_top_level_statement' 
.rvm/rubies/ruby-head/lib/ruby/2.4.0/irb/ruby-lex.rb:231:in `catch' 
.rvm/rubies/ruby-head/lib/ruby/2.4.0/irb/ruby-lex.rb:231:in `each_top_level_statement' 
.rvm/rubies/ruby-head/lib/ruby/2.4.0/irb.rb:485:in `eval_input' 
.rvm/rubies/ruby-head/lib/ruby/2.4.0/irb.rb:395:in `block in start' 
.rvm/rubies/ruby-head/lib/ruby/2.4.0/irb.rb:394:in `catch' 
.rvm/rubies/ruby-head/lib/ruby/2.4.0/irb.rb:394:in `start' 
... 

Aber laufen (vorausgesetzt, ich habe einige Zeilen in der db)

run_block {Vehicle.all} 

Ich bekomme den Stack-Trace

.rvm/gems/ruby-head/gems/activerecord-4.2.7/lib/active_record/querying.rb:50:in `block (2 levels) in find_by_sql' 
.rvm/gems/ruby-head/gems/activerecord-4.2.7/lib/active_record/result.rb:51:in `block in each' 
.rvm/gems/ruby-head/gems/activerecord-4.2.7/lib/active_record/result.rb:51:in `each' 
.rvm/gems/ruby-head/gems/activerecord-4.2.7/lib/active_record/result.rb:51:in `each' 
.rvm/gems/ruby-head/gems/activerecord-4.2.7/lib/active_record/querying.rb:50:in `map' 
.rvm/gems/ruby-head/gems/activerecord-4.2.7/lib/active_record/querying.rb:50:in `block in find_by_sql' 
.rvm/gems/ruby-head/gems/activesupport-4.2.7/lib/active_support/notifications/instrumenter.rb:20:in `instrument' 
.rvm/gems/ruby-head/gems/activerecord-4.2.7/lib/active_record/querying.rb:49:in `find_by_sql' 
.rvm/gems/ruby-head/gems/activerecord-4.2.7/lib/active_record/relation.rb:639:in `exec_queries' 
.rvm/gems/ruby-head/gems/activerecord-4.2.7/lib/active_record/relation.rb:515:in `load' 
.rvm/gems/ruby-head/gems/activerecord-4.2.7/lib/active_record/relation.rb:243:in `to_a' 
.rvm/gems/ruby-head/gems/activerecord-4.2.7/lib/active_record/relation.rb:630:in `inspect' 
.rvm/rubies/ruby-head/lib/ruby/2.4.0/irb/inspector.rb:109:in `block in <module:IRB>' 
.rvm/rubies/ruby-head/lib/ruby/2.4.0/irb/inspector.rb:102:in `inspect_value' 
.rvm/rubies/ruby-head/lib/ruby/2.4.0/irb/context.rb:384:in `inspect_last_value' 
.rvm/rubies/ruby-head/lib/ruby/2.4.0/irb.rb:661:in `output_value' 
.rvm/rubies/ruby-head/lib/ruby/2.4.0/irb.rb:490:in `block (2 levels) in eval_input' 
.rvm/rubies/ruby-head/lib/ruby/2.4.0/irb.rb:623:in `signal_status' 

, die nicht zeigt, dass der Block in der Methode run_block irgendwo in der Ablaufverfolgung geschachtelt wurde. Running run_block {puts Vehicle.all} zeigt jedoch die run_block-Funktion in der Stack-Ablaufverfolgung. Außerdem zeigt Vehicle.first im Block den erwarteten Stack-Trace. Noch Vehicle.test_stack oben definiert zeigt weder die run_block Methode noch die test_stack Methode im Stack-Trace. Kann jemand diese Inkonsistenz erklären? Ist das ein Ergebnis von Ruby-Sprachkonstrukten oder etwas mit der Implementierung der .all Methode? Danke allen!

Antwort

1

Dies ist ein schwieriger. Der instantiate Stack-Trace zeigt run_block nicht, weil instantiate nicht genannt von innerhalb run_block ist.

Hier ist der Deal: Wenn Sie Vehicle.all nennen, ist es nicht eine Reihe von Vehicle Objekte zurückgibt, von denen jeder instanziiert worden ist; Es gibt nur ein Objekt ActiveRecord::Relation zurück, das die noch auszuführende Abfrage darstellt. Da dies der letzte Ausdruck im Block ist, der an run_block übergeben wurde, ist es der Rückgabewert des Blocks, und da yield die letzte Anweisung in run_block ist, ist das ActiveRecord::Relation Objekt auch der Rückgabewert dieser Methode.

Wenn Sie jedoch eine Methode von IRB aufrufen, wird es zurückgegeben, in Form der Ergebnisse des Aufrufs Object#inspect darauf, und wenn Sie inspect auf ein ActiveRecord :: Relation-Objekt aufrufen, versucht es zu enthalten in der Ausgabe die Zeichenfolgen, die jedes Objekt in der Beziehung darstellen. Um do zu tun, muss es tatsächlich die Abfrage ausführen und die Model-Objekte instanziieren, so tut es, und Ihre instantiate override wird schließlich aufgerufen. Aber das ist, nachdem der run_block Block bereits verlassen wurde!

In der Tat, wenn Sie (in IRB) run_block {Vehicle.all} ; nil in einer einzigen Zeile ausgeführt haben, würde IRB nil als Ergebnis der Auswertung, und Ihre instanziate-Methode würde nie überhaupt aufgerufen werden.

Wenn Sie run_block { puts Vehicle.all } verwenden, dann ist die Relation-Objekt hat für die Puts realisiert tatsächlich sein, die Platz im Inneren des Blocks erfolgt so in diesem Fall, dass Sie run_block im Stack-Trace sehen tun.

+0

Wow, komplizierter als erwartet. Danke für die Antwort! – SJH

+0

@SJH der Grund, warum ActiveRecord zu diesem Problem geht, ist es, Aufrufe von Beziehungen zu verketten, wie in den Antworten auf diese Frage ganz schön erklärt: http://stackoverflow.com/questions/10747106/how-does-rails-activerecord-chain -where-Klauseln-ohne-mehrere-Abfragen – philomory