Những công dụng "ẩn" của pry không phải ai cũng biết

Pry là một công cụ nổi tiếng mà bất cứ lập trình viên Ruby nào cũng biết. Cách sử dụng rất đơn giản, chỉ cần thêm dòng lệnh binding.pry vào bất cứ đoạn code nào, chương trình sẽ dừng lại khi đọc đến đoạn code đó, và ta có thể sử dụng pry console như sau:

From: lib/dry/types/hash/schema.rb @ line 58 Dry::Types::Hash::Schema#try:

    40: def try(hash, &block)
    41:   success = true
    42:   output  = {}
    43:
    44:   begin
    45:     result = try_coerce(hash) do |key, member_result|
    46:       success &&= member_result.success?
    47:       output[key] = member_result.input
    48:
    49:       member_result
    50:     end
    51:   rescue ConstraintError, UnknownKeysError, SchemaError => e
    52:     success = false
    53:     result = e
    54:   end
    55:
    56:   binding.pry
    57:
 => 58:   if success
    59:     success(output)
    60:   else
    61:     failure = failure(output, result)
    62:     block ? yield(failure) : failure
    63:   end
    64: end

> (#<Dry::Types::Hash::Weak>)

Nhưng pry không chỉ có vậy, nó còn cung cấp những công cụ khác giúp cho việc tìm hiểu về code được chủ động và hiệu quả hơn. Dưới đây là một số tính năng hỗ trợ của pry mà một lập trình viên nên biết:

Liệt kê các method khả dụng

Pry console cung cấp một lệnh ls có khả năng liệt kê các hàm và biến khả dụng trong scope hiện tại. Ví dụ với pry console hiện lên ở ví dụ trên, lệnh ls sẽ in ra:

> (#<Dry::Types::Hash::Weak>) ls
#<Dry::Equalizer:0x007fafd29f2b88>#methods:
  hash
  inspect

Dry::Equalizer::Methods#methods:
  ==
  eql?

Dry::Types::Options#methods:
  meta
  pristine
  with

Dry::Types::Builder#methods:
  constrained
  constrained_type
  constructor
  default
  enum
  optional
  safe
  |

Dry::Types::Definition#methods:
  ===
  default?
  name
  options
  primitive?
  success
  constrained?
  failure
  optional?
  primitive
  result
  valid?

Dry::Types::Hash#methods:
  permissive
  schema
  strict
  strict_with_defaults
  symbolized
  weak

Dry::Types::Hash::Schema#methods:
  []
  call
  member_types

Dry::Types::Hash::Weak#methods:
  try

instance variables:
  @__args__
  @member_types
  @meta
  @options
  @primitive

locals:
  block
  e
  failure
  hash
  output
  result
  success

Đây là một danh sách liệt kê các hàm khả dụng trong scope hiện tại, được nhóm theo các class và module định nghĩa hàm đó. Ở phía dưới là danh sách các biến instance và local. Nhờ đó ta có thể nắm được sơ bộ vai trò và nhiệm vụ của đoạn code đang debug.

Lệnh ls còn cho phép ta đào sâu vào các phần khác của scope. Ta có thể thêm tùy chọn -locals, hay viết tắt -l để xem tên của các biến local cùng với giá trị hiện tại của chúng:

> (#<Dry::Types::Hash::Weak>) ls -l
result = {
  :name=> #<Dry::Types::Result::Failure
    input=nil
    error=#<Dry::Logic::Result:0x007fafd2cb98d0
      @success=false,
      @id=nil,
      @serializer=#<Proc:[email protected]/dry/logic/rule.rb:47>>>}
hash = {:name=>nil}
output = {:name=>nil}
success = false
block = nil
e = nil
failure = nil

Tìm hiểu về code khi không có documentation

Pry cung cấp công cụ hỗ trợ việc tìm kiếm và liệt kê các hàm của một namespace bất kỳ. Ví dụ, nếu muốn tìm các hàm liên quan đến xử lý xpath của Nokogiri, ta dùng lệnh find-method:

> find-method xpath Nokogiri

Nokogiri::CSS.xpath_for
Nokogiri::CSS::Node
Nokogiri::CSS::Node#to_xpath
Nokogiri::CSS::Parser
Nokogiri::CSS::Parser#xpath_for
Nokogiri::XML::Document
Nokogiri::XML::Document#implied_xpath_contexts
Nokogiri::XML::Node
Nokogiri::XML::Node#implied_xpath_contexts
Nokogiri::XML::NodeSet
Nokogiri::XML::NodeSet#xpath
Nokogiri::XML::NodeSet#implied_xpath_contexts
Nokogiri::XML::Searchable
Nokogiri::XML::Searchable#xpath
Nokogiri::XML::Searchable#xpath_at
Nokogiri::XML::Searchable#xpath_query_from_css_rule

Từ kết quả tìm kiếm ta có thể học được một số điều khá thú vị:

  1. Ta có thể convert một CSS selector thành XPath
  2. Ta có thể search trên một XML document với hàm #xpath và #xpath_at...

Nếu muốn tìm hiểu thêm và nắm được chính xác cách sử dụng một hàm ta có thể sử dụng lệnh stat:

> stat Nokogiri::CSS.xpath_for
Method Information:
--
Name: xpath_for
Alias: None.
Owner: #<Class:Nokogiri::CSS>
Visibility: public
Type: Bound
Arity: -2
Method Signature: xpath_for(selector, options=?)
Source Location: /dev/gems/ruby/2.4.1/gems/nokogiri-1.7.2/lib/nokogiri/css.rb:22

Thậm chí ta có thể thấy rõ được phương thức hoạt động của hàm với lệnh show-source:

> show-source Nokogiri::CSS.xpath_for

From: /dev/gems/ruby/2.4.1/gems/nokogiri-1.7.2/lib/nokogiri/css.rb @ line 22:
Owner: #<Class:Nokogiri::CSS>
Visibility: public
Number of lines: 3

def xpath_for(selector, options={})
  Parser.new(options[:ns] || {}).xpath_for selector, options
end

Hoặc ta có thể xem doc của hàm nếu có với lệnh show-doc:

> show-doc Nokogiri::XML::Searchable#xpath

From: /dev/gems/ruby/2.4.1/gems/nokogiri-1.7.2/lib/nokogiri/xml/searchable.rb @ line 122:
Owner: Nokogiri::XML::Searchable
Visibility: public
Signature: xpath(*args)
Number of lines: 29

call-seq: xpath *paths, [namespace-bindings, variable-bindings, custom-handle-class]

Search this node for XPath paths. paths must be one or more XPath
queries.

  node.xpath('.//title')
  
A hash of namespace bindings may be appended. For example:

  node.xpath('.//foo:name', {'foo' => 'http://example.org'})
  node.xpath('.//xmlns:name', node.root.namespace)
  
A hash of variable bindingd may also be appended to namespace bindings. For example:

  node.xpath('.//address[@domestic=$value]', nil, {:value => 'Yes'})
  
Custom XPath functions may also be defined. To define custom
functions create a class and implement the function you want
to define. The first argument to the method will be the
current matching NodeSet. Any other arguments are ones that
you pass in. Note that this class may appear anywhere in the
argument list. For example:

  node.xpath('.//title[regex(., "\w+")]', Class.new {
    def regex node_set, regex
      node_set.find_all { |node| node['some_attribute'] =~ /#{regex}/ }
    end
  }.new)

>

Phía trên là những lệnh đơn giản và hữu ích khi debug với pry. Hy vọng bài viết này sẽ giúp ích được cho bạn trong công việc. Xin cảm ơn.