Como depurar scripts Ruby [fechado]

153

Copiei o seguinte código Ruby da Internet e fiz algumas alterações, mas ele não funciona.

O que posso fazer para depurar o programa sozinho?

Andrew Grimm
fonte
1
Votação para reabrir. O OP claramente deseja a depuração de etapas do tipo GDB.
Ciro Santilli escreveu

Respostas:

145

Use Pry ( GitHub ).

Instalar via:

$ gem install pry
$ pry

Adicione então:

require 'pry'; binding.pry

no seu programa.

A partir de pry0.12.2 no entanto, existem comandos nenhuma navegação, como next, break, etc. Algumas outras pedras preciosas, adicionalmente, fornecer isso, ver, por exemplo pry-byedebug.

horseyguy
fonte
10
Também recomendo usar o Pry (definitivamente um trocador de vida!). Depois de instalado e necessário no seu programa, definir um ponto de interrupção é tão fácil quanto escrever binding.pry. Ele também vem com conclusão cor, pesquisa de documentação, e possibilidade de editar dinamicamente e recarregar um método ..
Andrea Fiore
5
A página inicial de Pry está disponível aqui: pryrepl.org .
shadowbq
4
Pry/ byebugsão ótimos, mas não como seu primeiro passo na depuração. Na maioria dos casos, gerar uma exceção raise object.inspectresolverá seu problema mais rapidamente do que abrir uma sessão irb. Eu recomendo usar apenas os depuradores de console, uma vez que soluções simples, como gerar uma exceção, não conseguem resolver o seu problema.
Kelsey Hannan
3
Existe uma maneira de codificar uma etapa pry? Não consegui descobrir como fazê-lo; e é isso que espero de um depurador.
jpetazzo
2
@jpetazzo sim, digitando 'próximo'
marcbest
114
  1. Em Ruby:

    ruby -rdebug myscript.rb 

    então,

    • b <line>: colocar ponto de interrupção
    • e n(ext)ou s(tep)ec(ontinue)
    • p(uts) para exibição

    (como perl debug)

  2. No Rails: inicie o servidor com

    script/server --debugger

    e adicione debuggero código.

germanlinux
fonte
7
-rdebug é lixo. E sem manutenção. Use Pry (veja a outra resposta).
Snowcrash 22/03
3
@SnowCrash - Por que você diz que isso -r debugé lixo?
sid smith
8
com -rdebug: não há necessidade de arquivo de origem mudança, a fim de depuração
germanlinux
2
Para um novo aplicativo Ruby / Rails, Pry é a resposta correta. Mas passei mais de uma hora tentando encontrar uma versão antiga do Pry para executar em um aplicativo Rails 2.2 com uma versão específica dos facetsrequisitos da gema e não tive êxito. Para aplicativos antigos do Rails, ruby-debugé um pouco desagradável, mas faz o trabalho.
Abe Voelker
56

Como corrimão recomendado: use alavanca! Só posso concordar com isso.

alavancar é um substituto muito melhor que o irb.

Você precisa adicionar

require 'pry'

ao seu arquivo de origem e insira um ponto de interrupção no seu código-fonte adicionando

binding.pry

no local em que você deseja dar uma olhada nas coisas (isso é como disparar um ponto de interrupção em um ambiente IDE clássico)

Uma vez que seu programa atinge o

binding.pry

Nesta linha, você será jogado diretamente para a réplica, com todo o contexto do seu programa à mão, para que você possa simplesmente explorar tudo ao redor, investigar todos os objetos, alterar estado e até mesmo alterar o código rapidamente.

Eu acredito que você não pode alterar o código do método em que está atualmente, portanto, infelizmente, não pode alterar a próxima linha a ser executada. Mas um bom código ruby ​​tende a ser uma linha única ;-)

edx
fonte
30

Depurar gerando exceções é muito mais fácil do que olhar através deprintinstruções de log e, para a maioria dos bugs, geralmente é muito mais rápido do que abrir um depurador irb comopryoubyebug. Essas ferramentas nem sempre devem ser seu primeiro passo.


Depurando Ruby / Rails rapidamente:

1. Método Rápido: Aumente um Exceptionentão e .inspectseu resultado

A maneira mais rápida de depurar o código Ruby (especialmente Rails) é raiseuma exceção ao longo do caminho de execução do seu código enquanto chama .inspecto método ou objeto (por exemplo foo):

raise foo.inspect

No código acima, raisedispara um Exceptionque interrompe a execução do seu código e retorna uma mensagem de erro que contém convenientemente .inspectinformações sobre o objeto / método (ou seja foo) na linha que você está tentando depurar.

Essa técnica é útil para examinar rapidamente um objeto ou método ( por exemplo, é nil? ) E para confirmar imediatamente se uma linha de código está sendo executada em um determinado contexto.

2. Fallback: Use um depurador IRB ruby como byebugoupry

Somente depois de obter informações sobre o estado do fluxo de execução de seus códigos, você deve considerar a mudança para um depurador ruby ​​gem irb como pryou byebugonde você pode se aprofundar no estado dos objetos em seu caminho de execução.


Conselho Geral para Iniciantes

Ao tentar depurar um problema, um bom conselho é sempre: Leia a mensagem de erro! @ # $ Ing (RTFM)

Isso significa ler as mensagens de erro com cuidado e completamente antes de agir para que você entenda o que está tentando lhe dizer. Ao depurar, faça as seguintes perguntas mentais, nesta ordem , ao ler uma mensagem de erro:

  1. Qual classe o erro faz referência? (ou seja , tenho a classe de objeto correta ou é meu objeto nil? )
  2. Qual método o erro faz referência? (ou seja, é um tipo no método; posso chamar esse método nesse tipo / classe de objeto? )
  3. Finalmente, usando o que posso deduzir das minhas duas últimas perguntas, quais linhas de código devo investigar? (lembre-se: a última linha de código no rastreamento da pilha não é necessariamente onde está o problema.)

No rastreamento da pilha, preste atenção especial às linhas de código que vêm do seu projeto (por exemplo, linhas começando com app/...se você estiver usando o Rails). 99% das vezes o problema está no seu próprio código.


Para ilustrar por que a interpretação nesta ordem é importante ...

Por exemplo, uma mensagem de erro do Ruby que confunde muitos iniciantes:

Você executa um código que, em algum momento, é executado da seguinte maneira:

@foo = Foo.new

...

@foo.bar

e você recebe um erro que afirma:

undefined method "bar" for Nil:nilClass

Os iniciantes veem esse erro e pensam que o problema é que o método não barestá definido . Não é. Nesse erro, a parte real que importa é:

for Nil:nilClass

for Nil:nilClasssignifica que @fooé nulo! @foonão é uma Foovariável de instância! Você tem um objeto que é Nil. Quando você vê esse erro, é simplesmente ruby ​​tentando dizer que o método barnão existe para objetos da classe Nil. (bem, duh! já que estamos tentando usar um método para um objeto da classe Foonão Nil).

Infelizmente, devido à forma como esse erro é escrito ( undefined method "bar" for Nil:nilClass), é fácil ser enganado ao pensar que esse erro tem a ver com o barser undefined. Quando não é lido com atenção, esse erro faz com que os iniciantes, por engano, procurem nos detalhes do barmétodo Foo, perdendo totalmente a parte do erro que sugere que o objeto é da classe errada (neste caso: nulo). É um erro que é facilmente evitado com a leitura completa das mensagens de erro.

Resumo:

Sempre leia cuidadosamente toda a mensagem de erro antes de iniciar qualquer depuração. Isso significa que: Sempre verifique a classe tipo de um objeto em uma mensagem de erro primeira , em seguida, seus métodos , antes de começar a sleuthing em qualquer stacktrace ou linha de código onde você acha que o erro pode estar ocorrendo. Esses 5 segundos podem economizar 5 horas de frustração.

tl; dr: Não aperte os olhos para imprimir logs: crie exceções ou use um depurador irb. Evite buracos de coelho lendo os erros cuidadosamente antes de depurar.

Kelsey Hannan
fonte
3
Embora eu concorde com você que a leitura dos logs de impressão seja entediante, acho que recomendar não usar um depurador em primeiro lugar é um péssimo conselho. Adicionar um ponto de interrupção e acioná-lo é exatamente o mesmo esforço que gerar uma exceção, mas fornece uma visão completa do estado atual do aplicativo. Se surgirem outras perguntas como resultado da inspeção do estado duvidoso, você poderá fazer uma busca interativa em vez de adicionar mais uma exceção e reiniciar o aplicativo.
Patrick R.
2
@PatrickR. Na minha experiência, os depuradores de IRB não são um bom primeiro passo quando você ainda está tentando detalhar exatamente onde está um problema no seu código. Adicionar e remover muitos pontos de interrupção do IRB leva mais tempo do que adicionar exceções e não fornece respostas definitivas sobre o fluxo de controle do seu código da maneira que as exceções podem, com os iniciantes, eliminar suposições ruins. Os pontos de interrupção do IRB também são fáceis de esquecer, causando confusão quando as solicitações são interrompidas. Se você sabe exatamente onde está um problema e precisa depurar seu estado, inicie com um depurador de IRB. Mas muitas vezes isso não é verdade.
Kelsey Hannan
1
Hah! Ler as mensagens de erro é útil para Ruby, mas outras linguagens de script? Não posso culpar o OP por nem mesmo se incomodar.
precisa saber é o seguinte
1
Minha reação inicial foi semelhante à @PatrickR., Mas tentei de qualquer maneira. No final, acho que esse é um conselho ruim, ou talvez da melhor maneira, um conselho adaptado a um caso de uso diferente do que eu tenho. Em particular, um caso em que (a) não está usando o Rails e (b) tem um resultado errado, mas não há erros ou exceções, ou seja, o código calcula um número, é apenas o número errado. Tentar adivinhar de forma iterativa onde criar um programa que de outra forma executaria uma falha é uma estratégia, mas é mais trabalhoso, pois tenho que continuar reiniciando e executando novamente. Cada ciclo tem seu próprio tempo de inicialização desperdiçado.
Brick
20
  1. Imprima as variáveis ​​sempre que possível. (Isso é chamado de depuração printf) Você pode fazer isso executando

    STDERR.puts x.inspect

    ou

    STDERR.puts "Variable x is #{x.inspect}"

    Se você quiser fazer isso mais fácil de escrever, então você pode querer usar o exemplor jóia.

  2. Ative os avisos. Se você estiver executando ruby, execute-o com o -winterruptor (por exemplo ruby -w script.rb). Se você estiver executando o irb e estiver usando uma versão do ruby ​​anterior à 1.9.2, digite $VERBOSE = trueno início da sua sessão. Se você digitar incorretamente uma variável de instância, quando os avisos estiverem ativados, você obterá

    aviso: variável de instância @valeusnão inicializada

  3. Entenda o conceito de uma divisão binária (a citação a seguir é de Práticas de um desenvolvedor ágil )

    Divida o espaço do problema ao meio e veja qual metade contém o problema. Em seguida, divida a metade pela metade novamente e repita.

  4. Se você for bem-sucedido com uma costela binária, poderá achar que há uma única linha que não faz o que você espera. Por exemplo

    [1, 2, 3].include?([1,2])

    dá um valor de false, mesmo que você pense que retornaria true. Nesse caso, você pode querer consultar a documentação. Os sites da Web para documentação incluem ruby-doc.org ou APIdock . Nesse último caso, você digitaria include?próximo à lupa no canto superior direito, escolheria o include?que está Arrayembaixo (se você não souber qual [1, 2, 3]é a classe , digite [1, 2, 3].classirb) e poderá incluir? (Matriz) , que descreve o que faz.

    No entanto, se a documentação não ajudar, é mais provável que você obtenha uma boa resposta se puder fazer uma pergunta sobre como uma linha específica não está fazendo o que deveria, e não por que um script inteiro não está fazendo o que deveria.

Andrew Grimm
fonte
7

apaga todas as coisas

Bem-vindo a 2017 ^ _ ^

Ok, se você não se opõe a experimentar um novo IDE, pode fazer o seguinte gratuitamente .

Instruções Rápidas

  1. Instalar vscode
  2. Instale o Ruby Dev Kit se você ainda não o tiver
  3. Instale as extensões Ruby, ruby-linter e ruby-rubocop para vscode
  4. Instale manualmente o que o gems rubyide / vscode-ruby especificar , se necessário
  5. Configurar o seu launch.jsonpara usar "cwd"e e "program" campos usando a {workspaceRoot}macro
  6. Adicione um campo chamado "showDebuggerOutput"e defina-o comotrue
  7. Habilite pontos de interrupção em todos os lugares nas suas preferências de Depuração como "debug.allowBreakpointsEverywhere": true

Instruções detalhadas

  1. Faça o download do Visual Studio Code aka vscode; isso não é o mesmo que o Visual Studio . É grátis, leve e geralmente considerado positivamente.
  2. Instale o Ruby Dev Kit; siga as instruções em seu repositório aqui: https://github.com/oneclick/rubyinstaller/wiki/Development-Kit
  3. Em seguida, você pode instalar extensões através de um navegador da Web ou dentro do IDE; isso é para dentro do IDE. Se você escolher o outro, você pode ir aqui . Navegue para a parte Extensões do vscode; você pode fazer isso de duas maneiras, mas o método mais adequado para o futuro provavelmente será acessado F1e digitado extaté que uma opção chamada Extensões: Instalar extensões seja disponibilizada. As alternativas são CtrlShiftxe, na barra de menus superior,View->Extensions
  4. Em seguida, você desejará as seguintes extensões; estes não são 100% necessários, mas deixarei você decidir o que manter depois de mexer em alguns:
    • Rubi; autor da extensão Peng Lv
    • rubi-rubocopo; autor da extensão misogi
    • rubi-linter; autor da extensão Cody Hoover
  5. Dentro do diretório do seu script ruby, vamos criar um diretório via linha de comando chamada .vscodee lá vamos apenas um arquivo chamado launch.jsononde vamos armazenar algumas opções de configuração.
    • launch.json conteúdo

{ "version": "0.2.0", "configurations": [ { "name": "Debug Local File", "type":"Ruby", "request": "launch", "cwd": "${workspaceRoot}", "program": "{workspaceRoot}/../script_name.rb", "args": [], "showDebuggerOutput": true } ] }

  1. Siga as instruções dos autores da extensão para instalações manuais de gemas. Ele está localizado aqui por enquanto: https://github.com/rubyide/vscode-ruby#install-ruby-dependencies
  2. Você provavelmente desejará colocar pontos de interrupção onde quiser; não ter esta opção ativada pode causar confusão. Para fazer isso, vamos para a barra de menus superior e selecionamos File->Preferences->Settings(ou Ctrl,) e rolamos até chegar à Debugseção. Expanda-o e procure um campo chamado "debug.allowBreakpointsEverywhere"- selecione esse campo e clique no pequeno ícone de lápis e defina-o como true.

Depois de fazer todas essas coisas divertidas, você poderá definir pontos de interrupção e depurar em um menu semelhante a este para meados de 2017 e um tema mais sombrio: insira a descrição da imagem aquicom todas as coisas divertidas, como sua pilha de chamadas, visualizador variável etc.

A maior PITA é 1) instalar as pré-solicitações e 2) Lembre-se de configurar o .vscode\launch.jsonarquivo. Somente o número 2 deve adicionar bagagem a projetos futuros, e você pode copiar uma configuração genérica o suficiente, como a listada acima. Provavelmente existe um local de configuração mais geral, mas não sei de nada.

kayleeFrye_onDeck
fonte
Agora é 2018, mas, infelizmente, você ainda não pode depurar um teste de unidade usando os plug-ins listados aqui ... Muito triste.
MonsieurDart
1
@MonsieurDart Quando escrevi isso, era para depuração básica de scripts ruby. Não estou familiarizado com nada específico sobre testes de unidade. Se esta resposta estiver incorreta ou desatualizada, informe o que precisa de atenção e qualquer informação que possa ajudar a acelerar esse processo.
kayleeFrye_onDeck
Hey @kayleeFrye_onDeck! Sua resposta é ótima, eu lhe dou totalmente isso. Mas, da última vez que verifiquei o VSC do plugin Ruby do Peng Lv, ele não foi capaz de definir pontos de interrupção em um teste de unidade (ou em qualquer outro teste). Foi uma das limitações conhecidas do plugin. Acho que as pessoas precisam saber disso antes de tentar configurar sua instância do VSC: leva tempo e, para muitas pessoas, a execução de testes é a principal maneira de escrever e depurar códigos. 😊
MonsieurDart
6

Eu recomendo fortemente este vídeo, a fim de escolher a ferramenta adequada no momento para depurar nosso código.

https://www.youtube.com/watch?v=GwgF8GcynV0

Pessoalmente, eu destacaria dois grandes tópicos neste vídeo.

  • Pry é incrível para depurar dados, "pry é um explorador de dados" (sic)
  • O depurador parece ser melhor depurar passo a passo.

Esses são meus dois centavos!

DavidSilveira
fonte
6

Todas as outras respostas já dão quase tudo ... Apenas uma pequena adição.

Se você deseja um depurador mais semelhante ao IDE (não CLI) e não tem medo de usar o Vim como editor, sugiro o plugin Vim Ruby Debugger para ele.

Sua documentação é bastante direta, então siga o link e veja. Em resumo, ele permite que você defina o ponto de interrupção na linha atual no editor, visualize as variáveis ​​locais na janela bacana em pausa, avance / avance - quase todos os recursos usuais do depurador.

Para mim, foi bastante agradável usar esse depurador vim para depurar um aplicativo Rails, embora as habilidades ricas em logger do Rails quase eliminem a necessidade dele.

NIA
fonte
6

Acabei de descobrir esta gema (transforma Pry em um depurador para MRI Ruby 2.0+)

https://github.com/deivid-rodriguez/pry-byebug

Instale com:

gem install pry-byebug

use exatamente como pry, marque a linha em que deseja quebrar:

require 'pry'; binding.pry

Ao contrário de baunilha de alavanca no entanto, esta jóia tem algumas GDB-como navegação comandos de teclas, como next, stepe break:

break SomeClass#run            # Break at the start of `SomeClass#run`.
break Foo#bar if baz?          # Break at `Foo#bar` only if `baz?`.
break app/models/user.rb:15    # Break at line 15 in user.rb.
break 14                       # Break at line 14 in the current file.
nurettin
fonte
5
  1. Você pode imprimir suas variáveis ​​ao longo do caminho
  2. Ative o -wsinalizador (avisos)
  3. Use uma ferramenta como ruby-debug
ghostdog74
fonte
2
Vou acrescentar que irbé um ótimo ponto de partida. Tente usar o irb com pequenos pedaços questionáveis. Eu amo o ruby-debug (ruby-debug19 para Ruby 1.9+) porque facilita parar o programa em execução, examinar variáveis, acessar o irb e continuar em execução.
the Tin Man
5

Para depurar facilmente o script do shell Ruby, basta alterar sua primeira linha de:

#!/usr/bin/env ruby

para:

#!/usr/bin/env ruby -rdebug

Sempre que o console do depurador for exibido, você poderá escolher:

  • cpara Continuar (para a próxima exceção, ponto de interrupção ou linha com:) debugger,
  • n para a próxima linha,
  • w/ wherepara Quadro de exibição / pilha de chamadas,
  • l para mostrar o código atual,
  • cat para mostrar pontos de captura.
  • h para mais ajuda.

Consulte também: Depurando com ruby-debug , Atalhos de chave para a ruby-debug gem .


Caso o script seja interrompido e você precise de um backtrace, tente usar lldb/ gdbcomo:

echo 'call (void)rb_backtrace()' | lldb -p $(pgrep -nf ruby)

e verifique o primeiro plano do processo.

Substitua lldbpor gdbse funciona melhor. Prefixo com sudoa depuração do processo que não é de propriedade.

kenorb
fonte
1
O ruby-debug parece bem datado. O repositório git para ruby-debug tem apenas um commit para este ano - ele ainda é mantido ativamente?
Andrew Grimm
Também parece vir embalado com rubi, o que é ótimo quando você está em uma pitada. Ótima resposta!
Breedly
5

A partir do Ruby 2.4.0, é mais fácil iniciar uma sessão do IRB REPL no meio de qualquer programa Ruby. Coloque estas linhas no ponto do programa que você deseja depurar:

require 'irb'
binding.irb

Você pode executar o código Ruby e imprimir variáveis ​​locais. Digite Ctrl + D ou quitpara finalizar o REPL e deixar o programa Ruby continuar em execução.

Você também pode usar putse pimprimir valores do seu programa enquanto ele está sendo executado.

David Grayson
fonte
4

Se você estiver usando RubyMine , a depuração de scripts ruby ​​é simples e direta.

Suponha que você tenha um script Ruby hello_world.rb

1. Defina pontos de interrupção

Defina um ponto de interrupção na linha 6 como abaixo.

insira a descrição da imagem aqui

2. Inicie a depuração

Agora você pode simplesmente iniciar o depurador para executar o script:

insira a descrição da imagem aqui

insira a descrição da imagem aqui

3. Inspecione variáveis, etc.

Então, quando a execução atingir um ponto de interrupção, você poderá inspecionar variáveis ​​etc.

insira a descrição da imagem aqui

Mais informações para sua referência

  1. Se você quiser usar o RubyMine para depuração remota , poderá fazê-lo.
  2. Se você deseja usar o RubyMine para depurar trilhos remotos em execução em uma janela de encaixe , também é simples.
Yuci
fonte
Temos uma maneira de depurar bibliotecas externas no RubyMine?
Am33d 14/10/19
2

depuração printf

Sempre houve uma controvérsia em torno das técnicas de depuração, algumas pessoas gostam de depurar por instruções impressas, outras gostam de se aprofundar em um depurador.

Eu sugiro que você tente as duas abordagens.

Na verdade, um dos antigos homens do Unix disse recentemente, que a depuração do printf era uma maneira mais rápida de ele em alguns momentos.

Mas se você é novo em algum trabalho e precisa entender um grande blob de código, é realmente útil avançar por lá, colocando alguns pontos de interrupção aqui e ali, acompanhando como ele funciona.

Isso deve lhe dar uma compreensão de como o código é tecido.

Se você é novo em algum software de outras pessoas, pode ajudá-lo a avançar por lá.

Você descobrirá rapidamente se eles organizaram de uma maneira inteligente ou se isso é apenas um monte de merda.

edx
fonte
este é sem dúvida a melhor resposta ,,, isso depende
menriquez
1

A mãe de todo o depurador é uma simples tela de impressão antiga. Na maioria das vezes, você provavelmente só deseja inspecionar alguns objetos simples, uma maneira rápida e fácil é a seguinte:

@result = fetch_result

p "--------------------------"
p @result

Isso imprimirá o conteúdo do @result para STDOUT com uma linha na frente para facilitar a identificação.

Bônus, se você usar uma estrutura capaz de carregar / recarregar automaticamente, como o Rails, nem precisará reiniciar seu aplicativo. (A menos que o código que você está depurando não seja recarregado devido a configurações específicas da estrutura)

Acho que isso funciona para 90% do caso de uso para mim. Você também pode usar o ruby-debug, mas acho que é um exagero na maioria das vezes.

Aaron Qian
fonte
1

Existem muitos depuradores com recursos diferentes, com base nos quais você faz a escolha. Minhas prioridades foram satisfeitas com movimentos de alavanca que eram:

  • informações rapidamente compreensíveis sobre como usar
  • etapas intuitivas (como entrar facilmente em blocos)
  • "recuar" (movimentos de alavanca satisfazem parcialmente a necessidade)
Daniel Garmoshka
fonte