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

3 cách để Monkey-Patch mà không gây ra lỗi

Khỉ vá. Khi bạn lần đầu tiên dùng thử Ruby, điều đó thật tuyệt vời. Bạn có thể thêm các phương thức vào ngay các lớp lõi! Bạn không phải gọi Time.now.advance(days: -1) , bạn có thể viết 1.day.ago ! Nó làm cho Ruby một niềm vui để đọc và viết. Cho đến khi…

Bạn gặp phải những lỗi kỳ lạ vì một bản vá đã thay đổi Hash .

Bạn bối rối không biết mã nào thực sự đã chạy, vì vậy bạn không thể gỡ lỗi khi mã bị hỏng.

Và cuối cùng bạn cũng nhận ra rằng tất cả các vấn đề của bạn là do 6 tháng trước, khi bạn vá Enumerable để làm cho một dòng mã ngắn hơn năm ký tự.

Nhưng lựa chọn thay thế là gì? Không tiện lợi chút nào? Mã trông giống như GroupableArray.new([1, 2, 3, 4]).in_groups_of(2) ? Đang đợi blank? để biến nó thành Ruby cốt lõi trước khi bạn được phép xử lý đầu vào của người dùng tốt?

Bạn không muốn từ bỏ hoàn toàn các bản vá lỗi khỉ. Nhưng làm thế nào bạn có thể viết các bản vá lỗi cho khỉ mà sẽ không khiến bạn muốn sa thải bản thân vì sự kém cỏi vào lần tiếp theo khi bạn nhìn thấy họ?

Đặt chúng vào một mô-đun

Khi bạn vá một lớp học, đừng chỉ mở lại lớp học và nhét bản vá của bạn vào đó:

class DateTime
  def weekday?
    !sunday? && !saturday?
  end
end

Tại sao không?

  • Nếu hai thư viện vá cùng một phương pháp, bạn sẽ không thể phân biệt được.

    Bản vá khỉ đầu tiên sẽ bị ghi đè và biến mất vĩnh viễn.

  • Nếu có lỗi, có vẻ như lỗi đã xảy ra bên trong DateTime .

    Mặc dù đúng về mặt kỹ thuật, nhưng nó không hữu ích lắm.

  • Thật khó để tắt các bản vá lỗi khỉ của bạn.

    Bạn phải nhận xét về toàn bộ bản vá của mình hoặc bỏ qua yêu cầu tệp vá lỗi khỉ của bạn nếu bạn muốn chạy mã mà không có nó.

  • Nếu bạn quên require 'date' trước khi chạy bản vá khỉ này, bạn sẽ vô tình xác định lại DateTime thay vì vá nó.

Thay vào đó, hãy đặt các bản vá lỗi khỉ của bạn trong một mô-đun:

module CoreExtensions
  module DateTime
    module BusinessDays
      def weekday?
        !sunday? && !saturday?
      end
    end
  end
end

Bằng cách này, bạn có thể sắp xếp các bản vá khỉ liên quan với nhau. Khi có lỗi, bạn phải xác định rõ chính xác mã sự cố đến từ đâu. Và bạn có thể đưa chúng vào từng nhóm một:

# Actually monkey-patch DateTime
DateTime.include CoreExtensions::DateTime::BusinessDays

Nếu bạn không muốn bản vá nữa, chỉ cần nhận xét dòng đó.

Giữ chúng cùng nhau

Khi bạn vá các lớp lõi, bạn thêm vào các API Ruby cốt lõi. Mỗi ứng dụng có các bản vá lỗi chính có một chút khác biệt. Vì vậy, bạn phải có cách để nhanh chóng tìm hiểu những thay đổi đó khi bạn chuyển sang cơ sở mã mới. Bạn phải biết nơi những con khỉ của bạn sống.

Tôi chủ yếu tuân theo quy ước vá khỉ của Rails. Các bản vá đi vào lib/core_extensions/class_name/group.rb . Vì vậy, bản vá này:

module CoreExtensions
  module DateTime
    module BusinessDays
      def weekday?
        !sunday? && !saturday?
      end
    end
  end
end

sẽ đi vào lib/core_extensions/date_time/business_days.rb .

Bất kỳ nhà phát triển mới nào cũng có thể duyệt qua các tệp Ruby trong lib/core_extensions và tìm hiểu những gì bạn đã thêm vào Ruby. Và họ sẽ thực sự sử dụng những phương pháp mới thuận tiện mà bạn đã viết, thay vì những phương pháp đó chỉ gây cản trở.

Hãy suy nghĩ về các trường hợp nguy hiểm

Tôi không biết tại sao Enumerable không có sum phương pháp. Vì vậy, tôi thường ước mình có thể viết [1, 2, 3].sum hoặc ["a", "b", "c"].sum hoặc [Article.new, Article.new, Article.new].sum … Ồ.

Khi bạn vá một lớp học, bạn thường nghĩ về một điều mà bạn muốn thực hiện dễ dàng hơn. Bạn muốn tính tổng các số, nhưng quên rằng Mảng có thể chứa những thứ khác.

Ngay bây giờ, nó có ý nghĩa. Bạn muốn không bao giờ cố gắng tính giá trị trung bình của một loạt các hàm băm. Nhưng khi bạn đính kèm các phương thức vào một đối tượng mà đôi khi bạn gọi chúng không thành công, bạn sẽ tự nhầm lẫn về sau.

Bạn có thể giải quyết vấn đề này bằng một số cách. Từ tốt nhất đến kém nhất:

  • Xử lý thông tin đầu vào không mong muốn một cách hợp lý.

    Điều này hoạt động tốt nếu bản vá của bạn hoạt động với chuỗi. Bạn có thể nhận được điều gì đó hợp lý từ hầu hết mọi thứ nếu bạn gọi to_s trên đó đầu tiên. Và Confident Ruby sẽ dạy bạn rất nhiều cách để đối phó với các loại đầu vào khác nhau.

  • Xử lý lỗi theo cách rõ ràng hơn.

    Điều này có thể dễ dàng như ném một ArgumentError với một thông điệp tốt khi bạn thấy thông tin đầu vào mà bạn không mong đợi. Đừng phụ thuộc vào người khác hiểu NoMethodError ngẫu nhiên s.

  • Ghi lại loại đầu vào mà bạn mong đợi trong một nhận xét.

    Hai tùy chọn còn lại tốt hơn, nếu bạn có thể sử dụng chúng. Nhưng nếu bạn không thể kiểm tra các trường hợp cạnh bên trong bản vá của mình, ít nhất hãy ghi lại chúng. Bằng cách đó, khi người gọi của bạn phát hiện ra chính bản vá lỗi của bạn đang gây ra sự cố của họ, họ sẽ biết bạn đang cố gắng làm gì.

Miếng dán khỉ yêu thích nhất mọi thời đại của tôi

Cuối cùng, tôi muốn để lại cho bạn bản vá khỉ yêu thích nhất mọi thời đại của tôi:Hash#string_merge :

lib / core_extensions / hash / merging.rb
module CoreExtensions
  module Hash
    module Merging
      def string_merge(other_hash, separator = " ")
        merge(other_hash) {|key, old, new| old.to_s + separator + new.to_s}
      end
    end
  end
end

{}.string_merge({:class => "btn"}) # => {:class=>"btn"}

h = {:class => "btn"} # => {:class=>"btn"}
h.string_merge({:class => "btn-primary"}) # => {:class=>"btn btn-primary"}

Nó giúp gắn các lớp CSS vào các phần tử HTML so đẹp hơn nhiều.

Vá khỉ nhạy bén

Các lớp lõi vá lỗi của khỉ không phải là xấu. Khi bạn làm tốt, nó sẽ làm cho mã của bạn giống Ruby hơn. Nhưng cũng giống như bất kỳ cạnh sắc nào của Ruby, bạn phải cẩn thận hơn khi sử dụng chúng.

Nếu bạn giữ các bản vá lỗi của mình lại với nhau, nhóm chúng thành các mô-đun và xử lý những điều không mong muốn, các bản vá lỗi khỉ của bạn sẽ an toàn nhất có thể.

Miếng dán khỉ tốt nhất là gì youve bao giờ được viết (hoặc nhìn thấy)? Để lại nhận xét và cho tôi biết về điều đó!