Seongsiks

Being A DevOpser. Powered by
Obtvse, highlight.js, theme toc Creative Commons License
Seongsiks Twitter Github Email
DevOps Ruby On Rails Chef Projects Misc Movies & Drama ME

Pry

주변에 계신 자바 개발자 분들이 배치잡으로 실행시킬 스크립트나 간편하게 Restful API을 구축하려고 할때 ruby를 많이 시도하고 계시다... 그런데 이클립스나 인텔리J같은 IDE를 이용한 디버깅을 하시다가 인터프리터 언어인 ruby디버깅을 하실때 어려움을 많이 겪으시는 듯해서 ruby디버깅 툴인 Pry를 소개하려고 한다.

뭐지?

Pry는 irb(Interactive Ruby shell)의 확장팩

그럼 irb는 뭘까..

python이나 lisp등 각종 스크립트 언어들이 가지고 있는 상호작용이 가능한 커맨트라인 툴로 커맨드 창에서 각종 코드를 실행해보기도 하고 클래스를 생성해보기도 하면서 개발 진행할 수 있도록 도와줍니다.

기능들

  • 코드보기
  • 클래스 분석하기
  • Syntax Highlight
  • 브레이크 포인트 넣기
  • 등등등

설치?

너무 간단해서...

$ gem install pry

팁들

실행중에 시작하기 - break point처럼

미리 코드에 break point처럼 pry를 호출해 놓고 해당 코드를 실행해서 해당 라인에서 pry console을 열어줘서 당시에 변수나 클래스등에 접근하고 새로운 코드를 작성하는 등의 작업을 통해 디버깅이 가능합니다.

# test.rb
require 'pry'

class A
  def hello() puts "hello world!" end
end

a = A.new

# set x to 10
x = 10

# start a REPL session
binding.pry

# program resumes here (after pry session)
puts "program resumes here. Value of x is: #{x}."

위 코드를 실행시키면 binding.pry를 만나는 순간 pry콘솔이 시작되고 class A을 열어보거나, a, x같은 로컬 변수도 접근 가능합니다.

pry(main)> a.hello
hello world!
=> nil
pry(main)> puts x
10
=> nil
pry(main)> def a.goodbye
pry(main)*   puts "goodbye cruel world!"
pry(main)* end
=> nil
pry(main)> a.goodbye
goodbye cruel world!
=> nil
pry(main)> x = "changed"
pry(main)> exit

# OUTPUT: program resumes here Value of x is: changed.

pry을 사용하다보면 화면 상에 각종 변수를 찍어보는 경우가 많은데 이때 puts메서드를 주로 사용하게 됩니다. 이를 대체해서 더 보기 좋게 프린트 해주는 awesome_print을 쓰면 훨씬 생산성을 올려 줄겁니다.

소스를 항해하기

pry를 이용해서 소스에서 여기저기 돌아 다닐 수 있는데, 단순히 파일을 열어서 편집하는 기능이 아니라, 클래스를 열어서 편집할 수 있는 아주 편리한 기능입니다. 적어도 내가 수정하고자 하는 클래스가 어느 경로에 어느 파일에 들어있는지 몰라도 바로 열 수 있습니다. 또 자동완성 기능이 없는 에디터를 사용하고 있을때, 특정 클래스의 메서드가 어떤것이 있나도 확인할 수 있습니다.

cd

리눅스에서 디렉토에 들어가듯이 클래스 안으로 들어가는데 사용하는 메서드로 스콥을 해당 클래스로 이동시키는 기능.

[8] pry(main)> cd Node # 노드 클래스로 들어가기
[8] pry(main)> ls -m # 노드 클래스의 메서드 조회
[8] pry(main)> ls -l # 로컬변수 조회
[8] pry(main)> edit find # find method 편집화면 열기
[8] pry(main)> cd .. # 상위로 나가기

ls

ls메서드는 오브젝트의 정보를 조회해 주는 메서드입니다. 개인적으로 자주 사용하는 명령어

    ls [-m|-M|-p|-pM] [-q|-v] [-c|-i] [Object]
    ls [-g] [-l]

options:

    -m, --methods        Show public methods defined on the Object (default)
    -M, --module         Show methods defined in a Module or Class
    -p, --ppp            Show public, protected (in yellow) and private (in green) methods
    -q, --quiet          Show only methods defined on object.singleton_class and object.class
    -v, --verbose        Show methods and constants on all super-classes (ignores Pry.config.ls.ceiling)
    -g, --globals        Show global variables, including those builtin to Ruby (in cyan)
    -l, --locals         Show locals, including those provided by Pry (in red)
    -c, --constants      Show constants, highlighting classes (in blue), and exceptions (in purple)
    -i, --ivars          Show instance variables (in blue) and class variables (in bright blue)
    -G, --grep           Filter output by regular expression
    -h, --help           Show help

Example
  • 현재 스콥에서 모든 상수 보기
[1] pry(main)> ls -c
constants:
  AbstractController       Fiber                      Object               SignalException
  ActionController         FiberError                 ObjectSpace          SimpleCov
  ActionDispatch           Fiddle                     OpenSSL              SimpleDelegator
  ActionMailer             File                       OpenStruct           Sinatra
  ActionPack               FileTest                   OptionParser         SingleForwardable
  ActionView               FileUtils                  OS                   Singleton
  ActiveModel              Fixnum                     ParseError           SizedQueue
  ActiveSupport            Float                      Pathname             Slop
  Addrinfo                 FloatDomainError           PDF                  Socket
  APP_PATH                 Forwardable                PP                   SocketError
  Arcfour                  GC                         Prawn                SortedSet
  ARGF                     GDBM                       PrettyPrint          Sprockets
  ArgumentError            GDBMError                  Proc                 SQLite3
  ARGV                     GDBMFatalError             Process              StandardError
  Array                    Gem                        ProgressBar          STDERR
  ...
  ...
[2] pry(main)>
  • Array 인스턴스 메서드 조회하기
[2] pry(main)> a = Array.new
=> []
[3] pry(main)> ls -m a
Enumerable#methods:
  all?            detect      each_with_index   find      index_by  max      min_by     one?          sort_by
  any?            each_cons   each_with_object  find_all  inject    max_by   minmax     partition     sum
  chunk           each_entry  entries           flat_map  lazy      member?  minmax_by  reduce        to_set
  collect_concat  each_slice  exclude?          group_by  many?     min      none?      slice_before
SimpleCov::ArrayMergeHelper#methods: merge_resultset
Array#methods:
  &            concat            flatten!       permutation           select        to_formatted_s
  *            count             forty_two      pop                   select!       to_json
  +            cycle             fourth         prepend               shelljoin     to_param
  -            deep_dup          from           pretty_print          shift         to_query
  <<           delete            frozen?        pretty_print_cycle    shuffle       to_s
  <=>          delete_at         grep           product               shuffle!      to_sentence
  ==           delete_if         hash           push                  size          to_xml
  []           drop              in_groups      rassoc                slice         transpose
  []=          drop_while        in_groups_of   reject                slice!        uniq
  append       each              include?       reject!               sort          uniq!
  as_json      each_index        index          repeated_combination  sort!         uniq_by
  assoc        empty?            insert         repeated_permutation  sort_by!      uniq_by!
  at           encode_json       inspect        replace               split         unshift
  blank?       eql?              join           reverse               take          values_at
  bsearch      extract_options!  keep_if        reverse!              take_while    zip
  clear        fetch             last           reverse_each          third         |
  collect      fifth             length         rindex                to
  collect!     fill              map            rotate                to_a
  combination  find_index        map!           rotate!               to_ary
  compact      first             original_grep  sample                to_csv
  compact!     flatten           pack           second                to_default_s
[4] pry(main)>
  • 두개 옵션 섞어서 원하는 메서드 찾기
[1] pry(main)> h = Hash.new
[2] pry(main)> ls -m --grep ^to_ h
Enumerable#methods: to_set
Hash#methods: to_a  to_h  to_hash  to_json  to_options  to_options!  to_param  to_query  to_s  to_xml
[3] pry(main)>

참고로 메서드를 찾는것은 find-method을 이용해서 쉽게 찾을 수도 있다.

[1] pry(main)> find-method <찾고자하는 메서드명> <obj - optional>

Shell 명령 날리기

pry콘솔에서 작업을 하다보면 리눅스 명령어를 실행 시키고 싶을때가 있는데 이때 콘솔을 종료하거나 하지 않고 바로 리눅스를 커맨드를 날릴 수 있습니다.

'.' prefix 사용하기

[1] pry(main)> .ls -l
Gemfile         Rakefile        config          db              public          vendor
Gemfile.lock    app             config.ru       lib             spec
README.md       bin             coverage        log             tmp
[2] pry(main)> a = "Gem"
=> "Gem"
[3] pry(main)> .ls -al | grep #{a} 
-rw-r--r--   1 kssminus  staff   1850 Sep 12 16:03 Gemfile
-rw-r--r--   1 kssminus  staff   5294 Sep 12 16:03 Gemfile.lock
[4] pry(main)> # 중간에 #{}을 이용해서 루비 변수를 넣을 수도 있습니다.

cat

pry에는 shell 명령어 cat이 어느정도 구현되어 있어서 .을 붙이지 않아도 비슷한 동작을 실행할 수 있습니다.

pry(main)> cat pry_instance.rb -l -s 470
470:     #   valid_expression?("class Hello") #=> false
471:     #   valid_expression?("class Hello; end") #=> true
472:     def valid_expression?(code)
473:       RubyParser.new.parse(code)
474:       true
475:     rescue Racc::ParseError, SyntaxError
476:       false
477:     end
478:   end
479: end

옵션 * -l 라인넘버를 출력할지 여부 * -t [file_type] 주어진 파일타입으로 신택스 하이라이팅 해주기 * -s -e 시작 라인 끝라인 * -i [range] 1..3과 같은 범위로 라인 넘버 주기

gem-cd

스콥을 젬으로 이동하기

[1] (pry) main: 0> .pwd
/Users/john/ruby/projects/pry-exception_explorer
[2] (pry) main: 0> gem-cd slop
[3] (pry) main: 0> .pwd
/Users/john/.rvm/gems/ruby-1.9.3-p125/gems/slop-2.4.4
[4] (pry) main: 0> 

입력하는 법

버퍼 비우기

콘솔을 사용하다보면 여러 라인의 코드를 넣기는 좀 부담스러울 경우가 많습니다. 이미 엔터를 친 상태에서 위쪽에 있는 라인을 수정할 수 없기때문인데요. 이를 보완하기 위한 방법으로 버퍼를 비우는겁니다.

pry(main)> def hello
pry(main)*   puts "hello"
pry(main)* !
Input buffer cleared!
pry(main)>

현재 버퍼 확인

지금까지 버퍼에 들어가 있는 내용을 확인하기. 이는 다음에 나올 버퍼 수정하기에서 사용하기에 앞서 필요한 버퍼내 라인넘버를 확인하는데 사용하시면 됩니다.

pry(main)> def poem
pry(main)*   puts "I remember silver hours and sunlight by the rivers"
pry(main)*   puts "And our kisses standing on the spicy plains"
pry(main)* show-input
1: def poem
2:   puts "I remember silver hours and sunlight by the rivers"
3:   puts "And our kisses standing on the spicy plains"
pry(main)*

버퍼 내용 수정하기

!은 버퍼 내용을 모두 날려버리기 때문에 타이핑의 압박을 받게 됩니다. 그래서 원하는 라인만 수정하고 싶을때는 amend-line을 사용하세요.

pry(main)> def goodbye
pry(main)*   puts "good evening"
pry(main)*   puts "au revoir"
pry(main)* amend-line 2..3 !
1: def goodbye
pry(main)*

편집기를 이용해 버퍼 수정하기

amend-line가지고도 좀 답답하다 하시는 분은 직접 에디터를 열어서 수정하셔도 됩니다. 밑도 끝도 없이 edit을 치세요.

pry(main)> def lorca
pry(main)*   puts "because dawn will throw fistfuls of ants at me"
pry(main)*   puts "cover me at dawn in a veil"
pry(main)* edit
Waiting for Emacs...  <editing takes place here in an external editor>
pry(main)* show-input
1: def lorca
2:   puts "cover me at dawn in a veil"
3:   puts "because dawn will throw fistfuls of ants at me"
4:   puts "and sprinkle my shoes with hard-water so the pincers of the scorpion slide"
pry(main)* 

리턴 감추기

루비는 마지막 라인을 암묵적으로 리턴하는 언어입니다. 그래서 그런지 콘솔에서 무언가 실행하게되면 연산결과를 콘솔에 출력해줍니다.(명시적으로 출력명령을 내리지 않아도) 굉장히 편리한 기능이기는 하지만 이 리턴값이 굉장히 길 경우 콘솔화면이 의미없는 출력으로 가득차게 됩니다. 이를 방지하기 위해 마지막에 ";"을 붙여주면 이를 생략할 수 있습니다.

pry(main)> "test" * 1000000
=> "testtesttesttesttest...test"
pry(main)> "test" * 1000000;
pry(main)>

재실행하기

pry는 독립적으로 사용자가 실행한 명령들의 히스토리를 관리하고 있습니다. 이를 다시 실행할 수 있습니다.

pry(main)> hist -tail 10
8197: ls -p
8198: pwd
8199: edit find
8200: .ls
8201: .ls -l
8202: .ls
8203: a = "Gem"
8204: .ls -al | grep #{a}
8205: irb
8206: hist
pry(main)> hist --replay 8203..8204
=> "Gem"
-rw-r--r--   1 kssminus  staff   1850 Sep 12 16:03 Gemfile
-rw-r--r--   1 kssminus  staff   5294 Sep 12 16:03 Gemfile.lock

_in_을 이용한 재실행

in은 pry가 관리해주는 배열과 비슷한 입력 버퍼로 play로 재실행을 할 수 있습니다.

[5] pry(main)> def poem
[5] pry(main)*   puts "come back to me brutal, empty room"
[5] pry(main)*   puts "thin byzantine face"
[5] pry(main)* end;  
[6] pry(main)> play --in 5 --lines 2..3
come back to me brutal, empty room
thin byzantine face
=> nil 

_out_에 저장된 리턴값들

가장 최근의 리턴값은 "_"으로 접근할 수 있는데 이는 마지막으로 리턴된 값으로 계속 치환되어 버립니다. 그러나 out은 기본적으로 100개의 리턴값을 보관해줍니다!!

pry(main)> 1 + 1
=> 2
pry(main)> 2 + 3
=> 5
pry(main)> _out_[-1] + _out_[-2]
=> 7

더 자세히 알고 싶다면..

우선 당연히 공식 홈페이지를 확인하면 됩니다. 그래도 제가 추천하는 더 익숙해 졌으면 하는 부분은..

pry 맛배기를 해주는 동영상들

comments powered by Disqus
Back to Ruby On Rails