Sức mạnh của Pry trong Rails
Bài đăng này đã không được cập nhật trong 6 năm
Hầu hết tất cả dev khi code project chạy bằng Rails đều sử dụng Pry là công cụ debug, cũng có các lệnh debug khác như là byebug nhưng Pry vẫn được sử dụng nhiều hơn cả.
Để cài đặt ta dùng lệnh
gem 'pry-rails', :group => :development
Câu nói thần thánh khi tìm lỗi đó là mở code và đặt binding.pry
. Vậy binding.pry
là gì?
Đó là lệnh khi trình biên dịch Ruby chạy tới dòng mà có lệnh đấy, thì nó sẽ tạm dừng xử lý và bắt đầu một trạng thái REPL (read-evaluate-print-loop), cho phép chúng ta gõ bất cứ code Ruby nào, sử dụng bất cứ biến nào ở những dòng trước đấy và hiển thị kết quả ngay trên terminal, kể cả bất cứ lệnh ruby hay các câu truy vấn database, thật tuyệt vời, nhờ đó giúp chúng ta dễ dàng debug hơn bao giờ hết.
Nhưng chúng ta đã từng tự hỏi lệnh đấy từ đâu đến không? Khi bật console, ta thấy
$ rails console
pry(main)> binding
=> #<Binding:0x007fee1297a308>
pry(main)> binding.class
=> Binding
như vậy, binding là 1 object, thuộc class Binding. Nhưng nó có thật là từ gem Pry không? nếu như bỏ gem pry-rails
$ DISABLE_PRY_RAILS=1 rails console
irb(main)> binding
=> #<Binding:0x007fb29c85c4a0>
vậy là object binding không thuộc gem pry-rails, ta sử dụng IRB
$ irb
irb(main)> binding
=> #<Binding:0x007fce81825f30>
thật ngạc nhiên khi binding là 1 object thuộc Ruby chứ không phải Rails.
Một vài tính năng hữu ích của Pry
Khi vào rails console, ta bắt đầu vào môi trường Pry
Pry có một vài lệnh rất hữu ích để ta tìm hiểu code, vd như show-doc
show-doc
pry(main)> show-doc binding
From: proc.c (C Method):
Owner: Kernel
Visibility: private
Signature: binding()
Number of lines: 10
Returns a Binding object, describing the variable and
method bindings at the point of call. This object can be used when
calling eval to execute the evaluated command in this
environment. See also the description of class Binding.
def get_binding(param)
return binding
end
b = get_binding("hello")
eval("param", b) #=> "hello"
show-doc
trả về tài liệu mô tả hàm mà chúng ta truyền vào, rất hữu ích để hiểu rõ công dụng của 1 hàm mới gặp.
Trong vd trên, ta thấy được rằng, binding thực sự là một object dùng để miêu tả một biến và trạng thái của nó tại vị trí gọi hàm.
vậy với binding.pry
thì sao
pry(main)> show-doc binding.pry
From: (...)/gems/pry-0.10.4/lib/pry/core_extensions.rb @ line 23:
Owner: Object
Visibility: public
Signature: pry(object=?, hash=?)
Number of lines: 18
Start a Pry REPL on self.
If `self` is a Binding then that will be used to evaluate expressions;
otherwise a new binding will be created.
param [Object] object the object or binding to pry
(__deprecated__, use `object.pry`)
param [Hash] hash the options hash
example With a binding
binding.pry
example On any object
"dummy".pry
example With options
def my_method
binding.pry :quiet => true
end
my_method()
@see Pry.start
ls
ta thấy, Pry chỉ là một hàm để bắt đầu một Pry REPL thông qua đối tượng binding.
Chúng ta có thể Pry mọi object. VD
pry(main)> our_string = "some TEXT!"
pry(main)> our_string.pry
pry("some TEXT!")>
pry("some TEXT!")> self
=> "some TEXT!"
pry("some TEXT!")> upcase
=> "SOME TEXT!"
pry("some TEXT!")> lowercase
NameError: undefined local variable or method `lowercase' for "some TEXT!":String
from (pry):32:in `__pry__'
pry("some TEXT!")> ls
(...)
Comparable#methods: < <= > >= between?
Colorize::InstanceMethods#methods: colorize colorized? uncolorize
String#methods:
% classify gray mb_chars rpartition to_c
* clear grayish next rstrip to_d
+ codepoints green next rstrip! to_date
ActiveSupport::ToJsonWithActiveSupportEncoder#methods: to_json
self.methods: __pry__
(...)
pry("some TEXT!")> ls -G case
String#methods: camelcase casecmp downcase downcase! swapcase swapcase! titlecase upcase upcase!
pry("some TEXT!")> downcase
=> "some text!"
pry("some TEXT!")> exit
=> nil
pry(main)> self
=> main
có rất nhiều lệnh hữu ích ở trên, khi gọi self , thì sẽ trả về đối tượng chúng ta Pry, gọi hàm upcase thì sẽ viết hoa object string, hàm lowercase thì không tồn tại, ta có thể
sử dụng ls
để lấy tất cả method của 1 object, nó tương tự như hàm methods
, nhưng ưu điểm ở chỗ là sẽ phân nhóm method theo class và namespace, nhờ vậy ta sẽ hiểu code sâu hơn, biết method này là của class nào.
Sử dụng thêm tham số -G
để giảm bớt kết quả, lệnh ls -G case
sẽ trả về tất cả method có chữ case, khi đó sẽ giúp chúng ta nhanh chóng tìm ra đúng tên hàm vì nhiều lúc ta không thể nhớ hết chính xác từng tên hàm được. Lệnh này tương tự khi ta dùng với grep string.methods | grep case
1 cách khác để tìm các method đó là sử dụng find-method
, thay vì phải pry 1 object , rồi chạy lệnh ls, ta có thể lấy ra các method của object đó trực tiếp từ main pry
pry(main)> find-method case our_string
String
String#camelcase
String#titlecase
String#casecmp
String#upcase
String#downcase
String#swapcase
String#upcase!
String#downcase!
String#swapcase!
cd
thay vì sử dụng string.pry, ta dùng cd
, ưu điểm ở chỗ là cd
có thể pry vào object theo kiểu lồng nesting
pry(main)> our_hash = { first: [1, 2, 3], second: "text" }
=> { :first => [1, 2, 3], :second => "text" }
pry(main)> cd our_hash
pry(#<Hash>):1> self
=> { :first => [1, 2, 3], :second => "text" }
pry(#<Hash>):1> cd self[:first]
pry(#<Array>):2> self
=> [1, 2, 3]
pry(#<Array>):2> nesting
Nesting status:
--
0. main (Pry top level)
1. #<Hash>
2. #<Array>
pry(#<Array>):2> jump-to 1
pry(#<Hash>):1> self
=> { :first => [1, 2, 3], :second => "text" }
pry(#<Hash>):1> cd self[:first]
pry(#<Array>):2> nesting
Nesting status:
--
0. main (Pry top level)
1. #<Hash>
2. #<Array>
pry(#<Array>):2> cd ..
pry(#<Hash>):1> self
=> { :first => [1, 2, 3], :second => "text" }
pry(#<Hash>):1> nesting
Nesting status:
--
0. main (Pry top level)
1. #<Hash>
pry(#<Hash>):1> cd /
pry(main)>
show-source
đây là 1 lệnh tôi rất thích, vì nhờ đó mà giúp tôi biết 1 hàm này thực hiện như thế nào, chứ ko phải mò mẫm chạy thử, hay phải lên mạng search cách sử dụng
pry(main)> show-source Array
From: (...)/lib/active_support/core_ext/array/access.rb @ line 1:
Class name: Array
Number of monkeypatches: 13. Use the `-a` option to display all available monkeypatches
Number of lines: 64
class Array
# Returns the tail of the array from +position+.
(...)
pry(main)> show-source Array#second
From: (...)/lib/active_support/core_ext/array/access.rb @ line 33:
Owner: Array
Visibility: public
Number of lines: 3
def second
self[1]
end
với tham số -a
, thì sẽ hiển thị tất cả các monkeypatches mà object có, giúp chúng ta sử dụng method cẩn thận hơn.
play
khi chúng ta debug trong controller, model hay object, nếu gặp phải 1 đoạn code nhiều dòng thì sẽ mất công phải copy từng dòng để check, thay vào đó ta có thể sử dung play
From: (...)/application_controller.rb @ line 7 ApplicationController#controller_method:
5: def controller_method
6: binding.pry
=> 7: puts "lots of code with"
8: puts "nestings and loops..."
9: end
pry(#<ApplicationController>)> play -l 7..8
lots of code with
nestings and loops...
nhờ vậy ta có thể chạy nhiều dòng một cách tuần tự.
show-model
Đó là 1 lệnh cực kỳ hữu ích khi ta muốn kiểm tra các attribute của 1 model, thay vì phải vào tìm trong file schema hoặc trong file model(trong trường hợp dùng gem để build các attribute vào trong file model)
pry(main)> show-model User
User
id: integer
birthday: datetime
address_name: string
created_at: datetime
updated_at: datetime
device_id: integer
province_id: integer
total_completed_order: integer
belongs_to :device
belongs_to :province
has_many :devices
has_many :orders
has_many :received_notifications (class_name :Notification, foreign_key :targetable_id)
vâng, kết quả trả về là toàn bộ attribute và các relation của model đó, rất dễ kiểm tra.
ngoài ra , nếu sử dụng thêm option -G
pry(main)> show-models -G name
User
id: integer
content: text
author_name: string # This will be highlighted due to the "name" grep
belongs_to: author
Province:
id: integer
name: string # This will be highlighted due to the "name" grep
sẽ trả ra tất cả các model có attribute chứa keyword chúng ta cần tìm.
show-routes
thay vì phải ra terminal, chạy lệnh rake routes | grep path
đợi khá lâu để lấy path vì nó phải load toàn bộ code thì ta có thể dùng trong console
pry(main)> show-routes -G login
login GET /login(.:format) sessions#index
rất nhanh gọn.
Trên đây là một vài công cụ hữu ích mà pry cung cấp, với những công cụ này, việc đọc code sẽ trở nên dễ dàng hơn bao giờ hết.
All rights reserved