Giter Club home page Giter Club logo

tabulo's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

tabulo's Issues

Colored output via rainbow

Heyya! First let me express my gratitude! Very nice gem and very nice documentation. ๐Ÿ‘

I am having only a little issue. I am using sickill/rainbow to colorize my output. For instance to highlight certain values or rows. Unfortunately the table gets scrambled, maybe because tabulo does not take the special characters into account, that are not displayed.

I don't know yet how to solve this most elegantly. Maybe just removing all /\e[[^m]+?m/ when accessing the length of the string.

2021-03-06-093108_1903x471_scrot

Edit: I just found out, that we need to do more I think, when tabulo wraps long headings oder cell data for instance the color goes til the end of the cell data string spanning multiple lines, causing the table border to be of the wrong color.

So I think we have to reset the terminal color explicitly.

Have a beautiful weekend!

Provide option to cap to terminal width automatically when shrinkwrapping

When calling .shrinkwrap!, the table width can currently be capped at a given number of characters using the max_table_width option. It would be good if that option could also take a parameter (such as :auto?) that automatically caps it at the current terminal width. This could be achieved using the tty-screen gem.

Table improperly formatted when `\r\n` is used on non-Windows machine

Reproduce with:

require "tabulo"

table = Tabulo::Table.new(["hello\r\nthere", "cool"], :itself, :length)

puts table

Output:

+--------------+--------------+
|    itself    |    length    |
+--------------+--------------+
        |           12 |
| there        |              |
| cool         |            4 |
+--------------+--------------+

Occurs on both Linux and Mac.

This issue crops up when e.g. using Tabulo to print the contents of a database table in which one of the columns was populated via an HTML textarea for which the styling whitespace: pre-wrap; applied. In that case, line breaks entered by the user are represented as \r\n regardless of operating system.

Generate table from array of structs

Hi,
I just want to make a table from an array of structs.
Here is an example :

require 'tabulo'

Pdata = Struct.new(:name, :age)

persons = []
persons << Pdata.new("Joe", 45)
persons << Pdata.new("Sam", 33)
persons << Pdata.new("Harvey", 72)
persons << Pdata.new("Jimmy", 14)

puts persons.class
puts persons

table = Tabulo::Table.new(???) do |t|
    t.add_column("Name", ???)
    t.add_column("Age", ???)
end

puts table

How can I do that ?
Thanks for your help.

The `max_table_width:` argument to `pack` is ignored if title is too long

If the table has a title, and it's longer than the argument passed to .pack(max_table_width: <here>) (or than the screen width if :auto is passed or if the argument is omitted), then the table will expand beyond the passed width. I don't think this is the behaviour that users of the library would expect; and it's not the documented behaviour. So I would characterise this as a bug.

To reproduce this:

require 'ostruct'
require 'tabulo'

data = [OpenStruct.new(foo: 'foovalue', bar: 'barvalue')]
table = Tabulo::Table.new(data, :foo, :bar, title: 'abcdefghijklmnopqrstuvwxyz 1234567890')
puts table.pack(max_table_width: 20)

Make the "shrinkwrap!" functionality more ergonomic

Calling puts table.shrinkwrap!(max_table_width: 120) is a fair bit of typing for something you might want to quickly bash out in the Rails console. Consider adding a shorter-named method, perhaps pack!, or just pack, that simply takes a single argument, e.g.:

puts table.pack(120)

Once this is added, consider deprecating shrinkwrap!.

Skip a row but keep headers

First of all, thank you for the great library.

I'm trying to print rows conditionally. To achieve this, I'm using table.each with next. The snippet follows.

Actual behavior: The problem is that headers seem to be tied to the first row. If the row is skipped, so are the headers.
Expected behavior: The headers should be shown regardless of the rows.

Code:

#!/usr/bin/env ruby

require 'tabulo'

table = Tabulo::Table.new([['one', true], ['two', false]], :first, :last)

puts "Without headers"
table.each do |row|
  next if row.source.last
  puts row
end

puts "With headers"
table.each do |row|
  next if !row.source.last
  puts row
end

Output:

Without headers
| two          |     false    |
With headers
+--------------+--------------+
|     first    |     last     |
+--------------+--------------+
| one          |     true     |

Allow table to be transposed

It would be good to have a Table#transpose method, that rotates the table 90 degrees, so that the previous headers form the new table's left-hand column, and then there is one additional column for each element of the original underlying enumerable. The headers for the new, transposed, table could perhaps be the string values of the original underlying enumerable elements; or an option could be provided to generate the header for each of the new table's columns by means of a callable that will be passed the element of the original underlying enumerable corresponding to that column.

Allow columns as varargs in Table initializer

Why not let the user type

Tabulo::Table.new(enumerable, :some_method, :some_other_method)

instead of

Tabulo::Table.new(enumerable, columns: [:some_method, :some_other_method])

?
The latter is more explicit, but I feel like the ergonomic advantages of the former more than make up for that.

Why is extractor proc mandatory on table instantiation/add_column()?? It doesn't make sense.

I've been mulling over your documentation and trying to get this to work for about 4 hours. It's late, too...so I might just be too tired to "get it." Now I'm reading your code and stuff ain't matching up.

First of all, I'm really angry right now so I apologize if I come across as short. Nothing should be this complicated and I will concede that perhaps I'm just not grasping the documentation.

Your documentation suggests that if I pass an enumerable as source then things should just work, but your code samples always use a proc to manipulate the data rather than just putting the data as-is in the cell. The more I read your code, the more it seems that I MUST pass a proc that knows how to extract the data to be stored in a column. Also, you mention ActiveRecord a couple of times, so is Tabulo only tailored for activerecord data? What about non-activerecord sources? Also, having some kind of reference source in the quick and full API sections would be super nice.

Here's my source data

[
    [ 0] {
            :profile => "some-profile",
        :instance_id => "i-xxxxxxxx",
               :name => "vagrantimages",
               :tags => {
                   "name" => "vagrantimages",
            "environment" => "dev"
        }
    },
    [ 1] {
            :profile => "some-profile",
        :instance_id => "i-xxxxxxxxxxx",
               :name => "more-images",
               :tags => {
                   "name" => "more-images",
            "environment" => "prod"
        }
    },
    {...}
]

Cool. My code:

require 'tabulo'

module UnusedEbs
  class Report
    def initialize(instances)
      @instances = instances
    end

    def generate
      @instances.each do |row|
        ebs_tags = []
        row[:tags].each_pair \
        {|k,v|
          ebs_tags << '%s=%s' % [k.to_s.yellow,v.green]
        }
        row[:tags] = ebs_tags.join("\n")
      end

      table = Tabulo::Table.new(@instances.to_enum,
                             title: 'Instances',
                             align_title: :center,
                             align_header: :center,
                             align_body: :left) do |t|
                t.add_column(nil, header: :profile)
                t.add_column(nil, header: 'instance-id')
                t.add_column(nil, header: :name)
                t.add_column(nil, header: :tags)
              end
      puts table.pack(max_table_width: 160)
    end
  end
end

But this throws:

.gem/gems/tabulo-2.7.0/lib/tabulo/table.rb:323:in `add_column': wrong number of arguments (given 0, expected 1) (ArgumentError)

I've used multiple permutations of table instantiation above including:

     puts Tabulo::Table.new(@instances.to_enum,
                            [:profile, :instance_id, :name, :tags],
                            # also without [] which doesn't work either but documentation suggests an array so I tried it...
                             title: 'Instances',
                             align_title: :center,
                             align_header: :center,
                             align_body: :left).pack(max_table_width: 160)

which throws:

.gem/gems/tabulo-2.7.0/lib/tabulo/table.rb:342:in `add_column': undefined method `to_proc' for [:profile, :instance_id, :name, :tags]:Array (NoMethodError)

and:

     puts Tabulo::Table.new(@instances.to_enum,
                             title: 'Instances',
                             align_title: :center,
                             align_header: :center,
                             align_body: :left).pack(max_table_width: 160)

which throws:

.gem/gems/tabulo-2.7.0/lib/tabulo/table.rb:503:in `pack': undefined method `left_padding' for nil:NilClass (NoMethodError)

Digging into that last error got me to looking at the source comment for the instantiator:

 33   | # @param [Enumerable] sources the underlying Enumerable from which the table will derive its data                                                                                                                                                                                              
 34   | # @param [Array[Symbol]] columns Specifies the initial columns. The Symbols provided must                                                                                                                                                                                                      
 35   | #   be unique. Each element of the Array  will be used to create a column whose content is                                                                                                                                                                                                     
 36   | #   created by calling the corresponding method on each element of sources. Note                                                                                                                                                                                                               
 37   | #   the {#add_column} method is a much more flexible way to set up columns on the table.

that's when I tried using t.add_column and in reading https://www.rubydoc.info/gems/tabulo/2.1.0/Tabulo/Table#:~:text=If%20the%20extractor%20argument%20is%20not%20also%20provided%2C%20then%20the%20label%20argument%20should%20correspond%20to%20a%20method%20to%20be%20called%20on%20each%20item%20in%20the%20table%20sources%20to%20provide%20the%20content%20for%20this%20column I realized that a proc is required to extract the data from the enumerable which simply doesn't make sense to me unless Tabulo only works with ActiveRecord data (but your documentation suggests otherwise). Why can't the source data just be iterated in order provided by default? In my use-case, I don't need or want to manipulate the data!

This causes unnecessary work for developers to use this library.

Furthermore, while your documentation covers the use of the proc, the requirement of the proc is lost I think. At the very least, it's not fully called out as mandatory. Your code samples always seem to use Ruby built-in methods to manipulate data, but as I've already said, what about just simply "extracting" the "stuff" from the source and placing it in the table? I just don't understand why any proc is needed for that functionality.

Implement a "quick" API

Consider providing an alternative, more ergonomic API for quickly generating "vanilla" tables where the columns simply correspond to methods. This could also "shrinkwrap" automatically, since that's usually what's desired anyway. Usage might look something like:

puts Tabulo.t(User.all, :id, :name, :email)

or even

Tabulo.p(User.all, :id, :name, :email)

to immediately print the table.

Can I wrap words for long column content?

I could not find any information in the docs or I am blind right now :)

Is it possible to let the column wrap before the word that is too long for the column?

So instead of:

+------------+
| Test       |
+------------+
| This is lo |
| ng         | 
+------------+

I would see:

+------------+
| Test       |
+------------+
| This is    |
| long       | 
+------------+

Cheerio!

Allow finer grained control of the resizings currently done by `.pack`

Following the discussion in #18, it seems it will be useful to allow users to have greater control over the action of Tabulo::Table#pack. The .pack method really does three different things:

  1. Autosizes columns to fit their contents
  2. Potentially shrinks columns as required for the table as a whole to fit the passed max_table_width: (or the terminal width if passed the default :auto)
  3. Potentially expands columns as required to accommodate a long title

Step 3 is buggy in that it can override the max_table_width. This is covered separately in #20. Step 3 should be ignored for the purposes of the present Issue.

For the present Issue, what we want is for the user to be able to enact 1 and 2 individually, and to modulate the action of these separately by passing an :except argument that could exclude certain columns from their action.

For example, the user should be able to do this:

table.autosize_columns.shrink_to(:screen, except: :id)

This would express that they want all columns to be just wide enough to accommodate their contents, but then have the table shrunk to be just wide enough for the screen width, with columns shrunk as required to permit this except for the :id column, which should never be shrunk. In other words, it would enable them to exclude the :id column from the shrinking-to-screen action, without excluding it from the autosize-to-contents action. This is something that is not possible using the except: parameter on pack that is currently implemented on the v2-8 branch.

Row styling

Dear Matt,

thanks for this awesome library. I just replaced a bunch of half-working custom table-printing code with Tabulo, and it's great.

There is one thing I could not figure out, however: how can I style a whole row depending on the row record?

My example is printing a list of hashes as a table. Each hash contains a :color key (for which I did not add a column). I would like to color the whole row in the specified color. How can I achieve this?

Make it easier to output the "same" table multiple times with different data sets

Currently each Tabulo::Table that is produced needs to be configured from scratch. If I'm outputting essentially the same table multiple times, with the same columns but with a different underlying enumerable, then it's not clear that there's a way to do this ergonomically, without going through the same motions of configuring a new table each time.

Options:

  1. Provide a "Tabulator" object that can be preconfigured with the same options as a Table (other than the underlying data set), and then used to generate Tables based on different data sets each time. E.g.: tabulator.tabulate(collection) or perhaps tabulator.call(collection).
  2. Simply expose and document a setter for updating the underlying collection on an existing table so it can be reused, e,g. table.data = new_collection.

Prevent wrapping/shrinking of specific columns when using `#pack`

hi,

nice gem! thanks for making it!

i have a use case where i'd like to use #pack, but prevent wrapping for one column.

this is for the gem invalid_record_finder, which finds broken rails records and reports them. one output format uses tabulo. sometimes the records have UUIDs, and if these UUIDs wrapped, its a pain to copy-paste them from the table.

example:

entry = {
  model:       'BlogPost',
  field:       'body_locale_en',
  field_value: 'hello! wassup?',
  message:     'Your input is too short (less than 20 chr)',
  id:          '36e8f2361c704977b06ab37a96901215',
}
table = Tabulo::Table.new([OpenStruct.new(entry)], *entry.keys)
puts table.pack.to_s

the output i get:

+----------+----------------+----------------+----------------+----------------+
|   model  |      field     |   field_value  |     message    |       id       |
+----------+----------------+----------------+----------------+----------------+
| BlogPost | body_locale_en | hello! wassup? | Your input is  | 36e8f2361c7049 |
|          |                |                | too short, les | 77b06ab37a9690 |
|          |                |                | s than 20 chr) | 1215           |
+----------+----------------+----------------+----------------+----------------+

the output i would like to achieve (id column doesn't wrap so it can be easily copy-pasted):

+----------+----------+----------+----------+----------------------------------+
|   model  |   field  | field_va |  message |                id                |
|          |          |    lue   |          |                                  |
+----------+----------+----------+----------+----------------------------------+
| BlogPost | body_loc | hello! w | Your inp | 36e8f2361c704977b06ab37a96901215 |
|          | ale_en   | assup?   | ut is to |                                  |
|          |          |          | o short  |                                  |
|          |          |          | (less th |                                  |
|          |          |          | an 20 ch |                                  |
|          |          |          | r)       |                                  |
+----------+----------+----------+----------+----------------------------------+

some ideas on how to achieve this:

  • add Column#min_width (optional Integer, default = 1)
    • i have implemented this in PR #17
    • good:
      • simple implementation
      • could be re-used for other use cases
    • bad:
      • not obvious that it only affects #pack output
      • cuts across concerns a bit (the column doesn't care about its own min_width internally)
      • leaves calculation of desired min_width to the user (entries.map { |e| e.id.length }.max)
  • add Column#wrap_on_pack (optional Boolean, default = true)
    • good:
      • makes it more explicit that it only affects #pack output
    • bad:
      • seems like more code needs to be touched to implement this
      • cuts across concerns a bit
      • hard to find a clear, non-obtrusive name?
  • add a keyword to #pack, something like no_shrink_columns: (optional Array, default = [])
    • good:
      • makes it most explicit that it only affects #pack output
    • bad:
      • seems to me to require the biggest amount of changes
      • hard to find a clear, non-obtrusive name?

edit: ... or maybe i missed a way that already allows doing this?!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.