Computer >> Máy Tính >  >> Lập trình >> Ruby

Hiểu về Ruby Refinements và Lexical Scope

Nếu bạn chưa bao giờ sử dụng các sàng lọc trước đây, có thể bạn sẽ ngạc nhiên. Bạn có thể đã nghe nói rằng các cải tiến đã được giới thiệu để thay thế khỉ vá. Vì vậy, bạn có thể mong đợi rằng bạn có thể triển khai một cái gì đó như hours của ActiveSupport phương pháp:

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)

Nếu bạn chạy đoạn mã trên, bạn sẽ thấy rằng nó không hoạt động. Vì vậy, những gì cho?

Giống như rất nhiều ý tưởng tuyệt vời khác, khái niệm ban đầu về sự sàng lọc đã phải được điều chỉnh để làm cho nó hoạt động với thực tế khó khăn.

Hành vi đáng ngạc nhiên trong đoạn mã mà chúng ta đã xem xét ở trên là do một trong những chỉnh sửa này gây ra. Cụ thể, quy tắc nói rằng các sàng lọc được xác định phạm vi từ vựng.

Phạm vi từ vựng là gì?

Khi chúng ta nói rằng điều gì đó là từ vựng, điều đó có nghĩa là liên quan đến văn bản - mã trên màn hình trái ngược với ý nghĩa của mã đó.

Nếu hai dòng có phạm vi từ vựng, điều đó đơn giản có nghĩa là chúng xuất hiện trong cùng một khối mã - bất kể khối mã đó có thể đánh giá như thế nào.

Xem ví dụ dễ hơn nhiều so với nói về nó:

class B
  # x and y share the same lexical scope
  x = 1
  y = 1
end

class B
  # z has a different lexical scope from x and y, even though it's in the same class. 
  z = 3
end

Các sàng lọc được xác định phạm vi từ vựng

Khi chúng tôi áp dụng sàng lọc với từ khóa using, sàng lọc chỉ hiển thị trong phạm vi từ vựng.

Đây là một ví dụ cho thấy ý tôi muốn nói:

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)

Mặc dù cả indexshow các phương thức là một phần của cùng một lớp, chỉ có index phương thức có quyền truy cập vào sàng lọc, vì chỉ nó chia sẻ phạm vi từ vựng với using tuyên bố.

Điều này có vẻ hơi kỳ lạ, bởi vì hầu hết mọi thứ khác trong Ruby đều được xác định phạm vi động. Khi bạn thêm một phương thức vào một lớp, phương thức vẫn ở đó khi bạn thoát khỏi định nghĩa lớp. Nhưng đó không phải là cách hoạt động của các sàng lọc.

Đây là một số hậu quả mà thoạt đầu có thể không rõ ràng.

Bạn không thể gọi động một phương pháp sàng lọc

send phương thức không được xác định trong cùng một khối mã có chứa câu lệnh using của bạn, nó không thể nhìn thấy sự sàng lọc.

Vì vậy, điều này không hoạt động:

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

Bạn không thể truy vấn sự tồn tại của một phương pháp sàng lọc

respond_to? phương pháp cũng không nằm trong cùng một khối mã. Vì vậy, nó không hoạt động vì cùng một lý do mà send phương pháp không.

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

Kết luận

Tôi hy vọng điều này sẽ giải tỏa một số nhầm lẫn mà tôi đã thấy mọi người có xung quanh việc sàng lọc. Chúng chắc chắn là một tính năng hữu ích tiềm năng của Ruby, nhưng nếu bạn chưa từng sử dụng chúng trước đây thì chúng có thể hơi phức tạp.