matt-harvey / tabulo Goto Github PK
View Code? Open in Web Editor NEWPlain text table generator for Ruby, with a DRY, column-based API
License: MIT License
Plain text table generator for Ruby, with a DRY, column-based API
License: MIT License
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.
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!
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.
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.
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.
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)
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!
.
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 |
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.
It seems https://travis-ci.org/github/matt-harvey/git_curate will stop working any day now. It needs replacing with some other way of running tests on push. (GitHub actions? CircleCI?)
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.
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.
I've removed Coveralls for now.
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.
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!
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:
max_table_width:
(or the terminal width if passed the default :auto
)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.
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?
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:
Table
(other than the underlying data set), and then used to generate Table
s based on different data sets each time. E.g.: tabulator.tabulate(collection)
or perhaps tabulator.call(collection)
.table.data = new_collection
.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:
Column#min_width
(optional Integer, default = 1)
#pack
outputmin_width
internally)min_width
to the user (entries.map { |e| e.id.length }.max
)Column#wrap_on_pack
(optional Boolean, default = true)
#pack
output#pack
, something like no_shrink_columns:
(optional Array, default = [])
#pack
outputedit: ... or maybe i missed a way that already allows doing this?!
Hi,
As of Ruby 2.4, Fixnum is deprecated.
To prevent the warning, line 144 in table.rb should be updated from
when Fixnum
to
when Integer
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.