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

Cách thoát khỏi chương trình Ruby

Với tư cách là nhà phát triển, chúng tôi dành quá nhiều thời gian để làm cho các chương trình của mình chạy đến mức dễ dàng bỏ qua cách chúng thoát ra. Và nó quan trọng! Khi các chương trình của bạn hoạt động chính xác khi thoát ra ngoài, điều đó sẽ khiến chúng dễ quản lý hơn rất nhiều và khiến chúng có nhiều khả năng hoạt động với các công cụ devops tiêu chuẩn hơn.

Có rất nhiều cách để bạn có thể thoát khỏi một chương trình Ruby. Tôi đã chỉ ra một số trong số chúng bên dưới. Trong bài đăng này, chúng tôi sẽ thảo luận chi tiết về từng thứ này và cách chúng có thể được sử dụng để biến ứng dụng của bạn trở thành một công dân unix hoạt động tốt.

# You can exit by calling a method
exit
exit!
abort("She cannot take any more of this, Captain!")

# ...or by failing to catch an exception
raise("Destroyed...")
fail

# ...or by letting the program end naturally. :)

Mã thoát và BẠN

Mọi chương trình bạn chạy trên Linux và OSX đều trả về mã trạng thái thoát khi nó chạy xong. Bạn thường không thấy mã thoát. Nó được hệ điều hành hậu trường sử dụng để xác định xem chương trình thoát bình thường hay có lỗi hay không.

Nếu bạn đang sử dụng bash, bạn có thể thấy mã thoát của chương trình mà bạn vừa chạy bằng cách kiểm tra $? biến môi trường. Ví dụ dưới đây hiển thị mã thoát cho một lần thất bại, sau đó cho một lần thành công.

% ls this-file-doesnt-exist
ls: this-file-doesnt-exist: No such file or directory
blog% echo $?
1
% ls .
bm.rb 
% echo $?
0

Nói chung, quy tắc cho các hệ thống dựa trên unix là số 0 có nghĩa là thành công. Nonzero có nghĩa là thất bại.

Nếu bạn đã từng sử dụng && "toán tử" trong bash, thì bạn đã sử dụng mã trạng thái. Trong ví dụ dưới đây, chúng tôi yêu cầu bash chỉ chạy chương trình B nếu chương trình A thành công.

% a && b

Bạn có thể sử dụng thủ thuật này theo nhiều cách. Ví dụ:bạn có thể muốn biên dịch trước nội dung và tải chúng lên CDN.

blog% rake assets:precompile && rake cdn:upload_assets

Chỉ định trạng thái thoát với Ruby

Ruby chăm sóc rất nhiều thứ về trạng thái thoát cho chúng ta. Nếu chương trình của bạn thoát bình thường, nó sẽ trả về trạng thái "thành công". Nếu nó không thành công do một ngoại lệ chưa được suy nghĩ, nó sẽ đưa ra trạng thái "fail". Dưới đây, tôi đang hiển thị mã thoát cho một tập lệnh đã tạo ra một ngoại lệ.

% ruby err.rb
err.rb:1:in `<main>': goodbye (RuntimeError)
tmp% echo $?
1

Nhưng điều gì sẽ xảy ra nếu bạn không muốn có bất kỳ trường hợp ngoại lệ nào chưa được cân nhắc? Điều gì sẽ xảy ra nếu bạn muốn thoát sạch nhưng vẫn trả về mã lỗi "không thành công"? Hơn nữa, nếu bạn muốn trả lại mã tùy chỉnh với thông báo lỗi thì sao?

May mắn thay, nó khá dễ dàng. Chỉ cần chuyển một đối số vào exit hàm số. Đối số có thể là boolean hoặc số nguyên. Nếu đó là boolean, thì true có nghĩa là thành công. Nếu đó là một số nguyên hơn 0 có nghĩa là thành công.

exit(true) # Exits with "success" code
exit(0) # Exits with "success" code

exit(false) # Exits with "failure" code
exit(1) # Exits with "failure" code

exit(436) # Custom failure error code

Cách exit hoạt động ngầm

Phương thức thoát chỉ đơn giản là tạo ra một SystemExit ngoại lệ. Nếu nó không được cập nhật, chương trình sẽ hủy bỏ giống như nó sẽ xảy ra với bất kỳ trường hợp ngoại lệ nào khác. Điều đó khá tuyệt, nhưng có một số hậu quả thú vị.

Nếu bạn nuốt tất cả các ngoại lệ SystemExit, bạn sẽ phá vỡ phương thức thoát. Tôi đã chỉ ra điều này trong ví dụ bên dưới.

begin
  exit 100
rescue SystemExit => e
  puts "Tried to exit with status #{ e.status }"
end

puts "...but it never exited, because we swallowed the exception"

# Outputs:
# Tried to exit with status 100
# ...but it never exited, because we swallowed the exception

Đây là một lý do khác để không bao giờ cứu Exception . Bởi vì SystemExit kế thừa từ Exception , nuốt Exception sẽ khiến lối ra bị phá vỡ.

begin
  exit
rescue Exception # never do this
  puts "I just broke the exit function!"
end

Thông báo lỗi mà con người có thể đọc được

Mặc dù mã thoát rất tốt cho máy móc, nhưng con người chúng ta thường thích văn bản giải thích một chút. May mắn thay, hệ điều hành cung cấp cho chúng ta một luồng đầu ra dành riêng cho những thứ như thông báo lỗi. Đúng, tôi đang nói về STDERR.

Bạn có thể ghi vào STDERR giống như bạn ghi vào bất kỳ đối tượng IO nào. Trong ví dụ dưới đây, tôi đang viết một thông báo lỗi và thoát ra với trạng thái "lỗi".

STDERR.puts("ABORTED! You forgot to BAR the BAZ")
exit(false)

Đây là Ruby, tất nhiên có một cách ngắn gọn hơn để viết vào stdout và thoát bằng mã lỗi. Chỉ cần sử dụng abort phương pháp.

# Write the message to STDERR and exit with an error status code.
abort("ABORTED! You forgot to BAR the BAZ")

Gọi lại qua at_exit

Ruby cho phép bạn đăng ký các trình xử lý có thể được gọi bất cứ khi nào chương trình thoát. Có thể có nhiều hơn một trong số chúng - chúng được gọi theo thứ tự ngược lại với chúng đã được đăng ký. Đây là những gì nó trông như thế nào:

at_exit do 
  puts "handler 1"
end

at_exit do 
  puts "handler 2"
end

# Outputs "handler2\nhandler1" on exit

Trình xử lý thoát có thể ghi đè mã thoát. Bạn thực hiện việc này bằng cách gọi exit từ bên trong trình xử lý. Mặc dù có vẻ như điều này sẽ gây ra một vòng lặp vô hạn, nhưng nó không. :)

at_exit do 
  exit 100
end

exit 0

# This program exits with a code of 100

Nếu bạn muốn thoát mà không gọi bất kỳ lệnh gọi lại nào, bạn có thể làm như vậy thông qua exit! phương pháp. Nhưng hãy nhớ rằng điều này có thể gây ra sự cố nếu bạn có đá quý hoặc mã của bên thứ ba khác phụ thuộc vào các lệnh gọi lại đó.

Một điều thú vị sang một bên

Hóa ra là rất nhiều thư viện sử dụng at_exit theo cách sáng tạo - và một số người có thể nói là những cách hacky. Ví dụ:Sinatra sử dụng at_exit hook vào như một cách để khởi động ứng dụng web. Điều này cho phép nó chắc chắn rằng tất cả mã của bạn đã được tải trước khi nó khởi động máy chủ. Đây là những gì nó trông giống như:

module Sinatra
  class Application < Base

   ...

  at_exit { Application.run! if $!.nil? && Application.run? }
end

Có một bài đăng tuyệt vời về điều này trên blog Arkency:Chúng ta có đang lạm dụng at_exit không?