Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Ruby

Ruby Refinements Explained: Lexical Scope ทำให้โค้ดทำงานอย่างไร

ไม่เคย用 refinements มาก่อน น่าประหลาดใจ คุณอาจได้ยิน refinements introduced เพื่อ replace monkey patching คาดว่า implement ActiveSupport hours method ได้:


module TimeExtension
  refine Fixnum do
    def hours
      self * 60
    end
  end
end

class MyFramework
  using TimeExtension
end

class MyApp < MyFramework
  def index
    1.hours
  end
end

MyApp.new.index # undefined method `hours' for 1:Fixnum (NoMethodError)

รันโค้ดข้างบน จะพบว่าไม่ work ทำไม?

เหมือน great ideas หลายอย่าง original concept refinements tweak ให้ work กับความเป็นจริง

Surprising behavior ข้างบนมาจาก tweak หนึ่ง: rule ว่า refinements are lexically scoped

Lexical Scoping คืออะไร?

พูดว่า something is lexical หมายถึงเกี่ยวกับ text — โค้ดบนหน้าจอ ไม่ใช่ความหมายของโค้ด

สองบรรทัด lexically scoped หมายถึงเกิดใน code block เดียวกัน ไม่ว่าจะ evaluate เป็นอะไร

ตัวอย่างง่ายกว่าอธิบาย:


class B
  # x และ y share same lexical scope
  x = 1
  y = 1
end

class B
  # z lexical scope ต่างจาก x และ y แม้ในคลาสเดียวกัน
  z = 3
end

Refinements are Lexically Scoped

Apply refinement ด้วย using keyword refinement visible เฉพาะ lexical scope

ตัวอย่างแสดง:


module TimeExtension
  refine Fixnum do
    def hours
      self * 60
    end
  end
end

class MyApp
  using TimeExtension
  def index
    1.hours
  end
end

class MyApp
  def show
    2.hours
  end
end

MyApp.new.show # undefined method `hour' for 1:Fixnum (NoMethodError)

แม้ index และ show อยู่ในคลาสเดียวกัน แค่ index เข้าถึง refinement ได้ เพราะแค่มัน share lexical scope กับ using statement

อาจแปลกเพราะ almost everything else ใน Ruby dynamically scoped เพิ่ม method ให้ class method อยู่ตอน exit class definition แต่ refinements ไม่ทำงานแบบนั้น

Consequences หลายอย่างไม่ชัดเจนตอนแรก

ไม่สามารถ Dynamically Invoke Refinement Method

เพราะ send method ไม่ได้ defined ใน code block เดียวกับ using statement มอง refinement ไม่เห็น

นี้ไม่ work:


class MyApp
  using TimeExtension
  def index
    1.send(:hours)
  end
end

ไม่สามารถ Query Existence of Refinement Method

respond_to? method ไม่อยู่ใน code block เดียวกัน ไม่ work เหมือน send


class MyApp
  using TimeExtension
  def index
    1.respond_to?(:hours)
  end
end

Conclusion

หวังช่วย clear up confusion เกี่ยว refinements พวกมัน potentially useful feature แต่ไม่เคย用มาก่อน tricky นิดหน่อย