Melhor maneira de imprimir um hash bonito

169

Eu tenho um grande hash com matrizes e hashes aninhados. Gostaria simplesmente de imprimi-lo para que fique 'legível' para o usuário.

Eu gostaria que fosse algo como to_yaml - isso é bem legível - mas ainda parece muito tecnológico.

Por fim, serão os usuários finais que precisam ler esses blocos de dados para que sejam formatados de maneira limpa.

Alguma sugestão?

Adam O'Connor
fonte
utilitário online jsonviewer.stack.hu . No entanto, ele não funciona corretamente para a sintaxe de hash rocket.
Amit Patel

Respostas:

256
require 'pp'
pp my_hash

Use ppse você precisar de uma solução integrada e apenas desejar quebras de linha razoáveis.

Use awesome_print se você pode instalar uma jóia. (Dependendo dos usuários, convém usar a index:falseopção para desativar a exibição dos índices da matriz.)

Phrogz
fonte
pp é legal, mas é realmente uma pena que não se possa limitar a profundidade.
akim 20/04
95

Se você possui JSON, eu recomendo, JSON.pretty_generate(hash)porque é mais simples que awesome_print , fica ótimo em uma pretag e facilita a cópia de uma página da web. (Veja também: Como posso "formatar" minha saída JSON em Ruby on Rails? )

David J.
fonte
Esta resposta se beneficiaria de um exemplo real
Travis Bear
@TravisBear Há um exemplo de saída se você clicar no link "ver também" na minha resposta. Eu recomendo esta resposta em particular: stackoverflow.com/a/1823885/109618
David J.
8
Seriaputs JSON.pretty_generate(hash)
joeloui
Se você precisar criar JSON, permita-me recomendar minha própria biblioteca (gratuita, OSS, sem anúncios) para criar bastante JSON a partir de Ruby ou JS: NeatJSON (Ruby) e NeatJSON (Online / JS)
Phrogz
Desculpe, agora percebo que pretty_generate aceita um objeto Ruby, não um texto json.
Tony
26

Outra solução que funciona melhor para mim do que ppou awesome_print:

require 'pry' # must install the gem... but you ALWAYS want pry installed anyways
Pry::ColorPrinter.pp(obj)
Alex D
fonte
2
Observe que Pry::ColorPrinter.pp(obj)grava no padrão, mas pode usar parâmetros adicionais, incluindo o destino. ComoPry::ColorPrinter.pp(obj, a_logger)
Eric Urban
Estou surpreso que isso não esteja melhor documentado: eu sempre uso o alavanca como meu console do Rails, e eu venho procurando há muito tempo como explorar sua bonita impressora sem usar outra gema. Promovido porque esta solução finalmente pôs fim à minha longa pesquisa. :-)
wiz
20

Se você não possui nenhuma ação sofisticada de gemas, mas possui JSON, esta linha de CLI funcionará em um hash:

puts JSON.pretty_generate(my_hash).gsub(":", " =>")

#=>
{
  :key1 => "value1",

  :key2 => "value2",

  :key3 => "value3"
}
Nick Schwaderer
fonte
8
Downvoted porque isso vai estragar todas as chaves e valores que contêm ":"
thomax
1
Isso também não lida com nulo (JSON) vs nulo (Ruby).
Rennex
1
Ainda é útil para a maioria das situações.
Abram
1
Não posso acreditar nisso três anos depois! Obrigado @Abram. :) Não é a solução mais elegante do mundo, mas faz as coisas de uma só vez.
Nick Schwaderer 8/10/19
4

Use as respostas acima se estiver imprimindo para os usuários.

Se você quiser imprimi-lo apenas no console, sugiro usar a alavanca de alavanca em vez do irb. Além da impressão bonita, o alavanca também possui muitos outros recursos (confira o railscast abaixo)

instalar alavanca

E verifique este railscast:

http://railscasts.com/episodes/280-pry-with-rails

Abdo
fonte
3

Fácil de fazer com o json, se você confiar que suas chaves são saudáveis:

JSON.pretty_generate(a: 1, 2 => 3, 3 => nil).
  gsub(": null", ": nil").
  gsub(/(^\s*)"([a-zA-Z][a-zA-Z\d_]*)":/, "\\1\\2:"). # "foo": 1 -> foo: 1
  gsub(/(^\s*)(".*?"):/, "\\1\\2 =>") # "123": 1 -> "123" => 1

{
  a: 1,
  "2" => 3,
  "3" => nil
}
mais grosseiro
fonte
1

Usando o Pry, você só precisa adicionar o seguinte código ao seu ~ / .pryrc:

require "awesome_print"
AwesomePrint.pry!
bartoindahouse
fonte
1

De todas as gemas que tentei, a show_datagem funcionou melhor para mim, agora a uso extensivamente para registrar hashes de params no Rails praticamente o tempo todo

Dr.Strangelove
fonte
0

Para hashes aninhados grandes, esse script pode ser útil para você. Ele imprime um hash aninhado em uma boa sintaxe semelhante a python, com apenas recuos para facilitar a cópia.

module PrettyHash
  # Usage: PrettyHash.call(nested_hash)
  # Prints the nested hash in the easy to look on format
  # Returns the amount of all values in the nested hash

  def self.call(hash, level: 0, indent: 2)
    unique_values_count = 0
    hash.each do |k, v|
      (level * indent).times { print ' ' }
      print "#{k}:"
      if v.is_a?(Hash)
        puts
        unique_values_count += call(v, level: level + 1, indent: indent)
      else
        puts " #{v}"
        unique_values_count += 1
      end
    end
    unique_values_count
  end
end

Exemplo de uso:

  h = {a: { b: { c: :d }, e: :f }, g: :i }
  PrettyHash.call(h)

a:
  b:
    c: d
  e: f
g: i
=> 3

O valor retornado é a contagem (3) de todos os valores de nível final do hash aninhado.

swilgosz
fonte
0

Aqui está outra abordagem usando json e rouge:

require 'json'
require 'rouge'

formatter = Rouge::Formatters::Terminal256.new
json_lexer = Rouge::Lexers::JSON.new

puts formatter.format(json_lexer.lex(JSON.pretty_generate(JSON.parse(response))))

(analisa a resposta de, por exemplo RestClient)

Adobe
fonte
0

No Rails

Se você precisar

  • um Hash "bem impresso"
  • por exemplo, no Rails.logger
  • que, especificamente, roda inspect nos objetos no Hash
    • o que é útil se você substituir / definir o inspectmétodo em seus objetos como deveria

... então isso funciona muito bem! (E fica melhor, quanto maior e mais aninhado for o objeto Hash.)

logger.error my_hash.pretty_inspect

Por exemplo:

class MyObject1
  def inspect
    "<#{'*' * 10} My Object 1 #{'*' * 10}>"
  end
end

class MyObject2
  def inspect
    "<#{'*' * 10} My Object 2 #{'*' * 10}>"
  end
end

my_hash = { a: 1, b: MyObject1.new, MyObject2.new => 3 }

Rails.logger.error my_hash
# {:a=>1, :b=><********** My Object 1 **********>, <********** My Object 2 **********>=>3}

# EW! ^

Rails.logger.error my_hash.pretty_inspect
# {:a=>1,
#  :b=><********** My Object 1 **********>,
#  <********** My Object 2 **********>=>3}

pretty_inspectvem do PrettyPrint , que os trilhos incluem por padrão. Portanto, não há necessidade de gemas nem conversão para JSON.

Não nos trilhos

Se você não está no Rails ou se o acima falhar por algum motivo, tente usar require "pp"primeiro. Por exemplo:

require "pp"  # <-----------

class MyObject1
  def inspect
    "<#{'*' * 10} My Object 1 #{'*' * 10}>"
  end
end

class MyObject2
  def inspect
    "<#{'*' * 10} My Object 2 #{'*' * 10}>"
  end
end

my_hash = { a: 1, b: MyObject1.new, MyObject2.new => 3 }

puts my_hash
# {:a=>1, :b=><********** My Object 1 **********>, <********** My Object 2 **********>=>3}

# EW! ^

puts my_hash.pretty_inspect
# {:a=>1,
#  :b=><********** My Object 1 **********>,
#  <********** My Object 2 **********>=>3}

Um exemplo completo

Big ol' pretty_inspected exemplo Hash do meu projeto com o texto específico do projeto de meus objetos inspecionados editados:

{<***::******************[**:****, ************************:****]********* * ****** ******************** **** :: *********** - *** ******* *********>=>
  {:errors=>
    ["************ ************ ********** ***** ****** ******** ***** ****** ******** **** ********** **** ***** ***** ******* ******",
     "************ ************ ********** ***** ****** ******** ***** ****** ******** **** ********** is invalid",
     "************ ************ ********** ***** ****** ******** is invalid",
     "************ ************ ********** is invalid",
     "************ ************ is invalid",
     "************ is invalid"],
   :************=>
    [{<***::**********[**:****, *************:**, ******************:*, ***********************:****] :: **** **** ****>=>
       {:************=>
         [{<***::***********[**:*****, *************:****, *******************:**]******* :: *** - ******* ***** - *>=>
            {}},
          {<***::***********[**:*****, *************:****, *******************:**]******* :: *** - *>=>
            {}},
          {<***::***********[**:*****, *************:****, *******************:**]******* :: ********* - *>=>
            {}},
          {<***::***********[**:*****, *************:****, *******************:**]******* :: ********** - ********** *>=>
            {}},
          {<***::***********[**:*****, *************:****, *******************:**]******* :: ******** - *>=>
            {}},
          {<***::***********[**:*****, *************:****, *******************:**]******* :: **** - *******>=>
            {}},
          {<***::***********[**:*****, *************:****, *******************:**]******* :: *** - ********** ***** - *>=>
            {}}]}},
     {<***::**********[**:****, *************:**, ******************:*, ***********************:****] ******************** :: *** - *****>=>
       {:errors=>
         ["************ ********** ***** ****** ******** ***** ****** ******** **** ********** **** ***** ***** ******* ******",
          "************ ********** ***** ****** ******** ***** ****** ******** **** ********** is invalid",
          "************ ********** ***** ****** ******** is invalid",
          "************ ********** is invalid",
          "************ is invalid"],
        :************=>
         [{<***::***********[**:*****, *************:****, *******************:***]******* :: ****** - ** - ********>=>
            {}},
          {<***::***********[**:*****, *************:****, *******************:***]******* :: ****** - ** - ********>=>
            {}},
          {<***::***********[**:*****, *************:****, *******************:**]******* :: ****** - ** - *******>=>
            {}},
          {<***::***********[**:*****, *************:****, *******************:**]*********** :: ****>=>
            {}},
          {<***::***********[**:*****, *************:****, *******************:**]******* :: ****** - ** - *******>=>
            {}},
          {<***::***********[**:*****, *************:****, *******************:**]******* :: ****** - ** - *********>=>
            {}},
          {<***::***********[**:*****, *************:****, *******************:**]******* :: ****** - ** - *******>=>
            {:errors=>
              ["********** ***** ****** ******** ***** ****** ******** **** ********** **** ***** ***** ******* ******",
               "********** ***** ****** ******** ***** ****** ******** **** ********** is invalid",
               "********** ***** ****** ******** is invalid",
               "********** is invalid"],
             :**********************=>
              [{<***::*******************[**:******, ************************:***]****-************ ******************** ***: * :: *** - ***** * ****** ** - ******* * **: *******>=>
                 {:errors=>
                   ["***** ****** ******** **** ********** **** ***** ***** ******* ******",
                    "***** ****** ******** **** ********** is invalid"],
                  :***************=>
                   [{<***::********************************[**:******, *************:******, ***********:******, ***********:"************ ************"]** * *** * ****-******* * ******** * ********* ******************** *********************: ***** :: "**** *" -> "">=>
                      {:errors=>["**** ***** ***** ******* ******"],
                       :**********=>
                        {<***::*****************[**:******, ****************:["****** ***", "****** ***", "****** ****", "******* ***", "******* ****", "******* ***", "****"], **:""] :: "**** *" -> "">=>
                          {:errors=>
                            ["***** ******* ******",
                             "***** ******* ******"]}}}}]}}]}},
          {<***::***********[**:*****, *************:****, *******************:**]******* :: ****** - ** - *********>=>
            {}},
          {<***::***********[**:*****, *************:****, *******************:**]******* :: ****** - ** - *********>=>
            {}},
          {<***::***********[**:*****, *************:****, *******************:***]******* :: ****** - ** - ********>=>
            {}},
          {<***::***********[**:*****, *************:****, *******************:***]******* :: ****** - ** - **********>=>
            {}},
          {<***::***********[**:*****, *************:****, *******************:***]******* :: ****** - ** - **********>=>
            {}},
          {<***::***********[**:*****, *************:****, *******************:***]******* :: ****** - ** - **********>=>
            {}}]}}]}}
pdobb
fonte
-4

Em Rails, matrizes e hashes no Ruby possuem funções to_json internas. Eu usaria JSON apenas porque é muito legível em um navegador da web, por exemplo, Google Chrome.

Dito isto, se você está preocupado com a aparência muito "tecnológica", provavelmente deve escrever sua própria função que substitui os chavetas e chavetas nos seus hashes e matrizes por espaços em branco e outros caracteres.

Procure a função gsub para uma maneira muito boa de fazer isso. Continue brincando com diferentes personagens e diferentes quantidades de espaço em branco até encontrar algo que pareça atraente. http://ruby-doc.org/core-1.9.3/String.html#method-i-gsub

Sid
fonte
7
Matrizes e hashes não têm um método to_json interno, eles são adicionados pelo ActiveSupport do Rails.
Tom De Leu
É ainda pior do que o normal IRB / alavanca:{"programming_language":{"ruby":{},"python":{}}}
Darek Nędza
O OP não descartou o Rails
Will Sheppard