tablesmith 0.1.2 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +5 -0
- data/.travis.yml +1 -0
- data/Gemfile.lock +2 -2
- data/README.md +2 -2
- data/lib/tablesmith.rb +3 -2
- data/lib/tablesmith/active_record_source.rb +24 -21
- data/lib/tablesmith/hash_rows_base.rb +34 -0
- data/lib/tablesmith/hash_rows_source.rb +2 -32
- data/lib/tablesmith/{batch.rb → table.rb} +13 -4
- data/lib/tablesmith/version.rb +1 -1
- data/spec/{active_record_batch_spec.rb → active_record_table_spec.rb} +144 -114
- data/spec/array_table_spec.rb +14 -0
- data/spec/fixtures.rb +13 -9
- data/spec/{hash_rows_batch_spec.rb → hash_rows_table_spec.rb} +24 -24
- data/spec/hash_table_spec.rb +14 -0
- data/spec/{batch_spec.rb → table_spec.rb} +16 -16
- metadata +16 -13
- data/spec/array_batch_spec.rb +0 -14
- data/spec/hash_spec.rb +0 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 422060113ff10de9d77319825d7c0584b1389e3e
|
4
|
+
data.tar.gz: c2672e0136fe3ccad708644232db70a7f3112ddd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 362ffe3c1d3cb37c26a521de9eea14cc4bcfb5ec42534930486d05238ecb2fb1fb90621318eb2bab75d979d9040c274601626a456464317cd6f8e2d7e8f1e340
|
7
|
+
data.tar.gz: 7df00f39dc421aaa159900710dc2af3de41d4d8805a8936818fbca388d64cf3dca682cbfa08b900d28616cbd9dd10136979dd06d1214bf84a1155fe9fa606280
|
data/.rubocop.yml
ADDED
data/.travis.yml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
language: ruby
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -26,8 +26,8 @@ Happy to learn about something else already out there, but have struggled to fin
|
|
26
26
|
that doesn't require some sort of setup. I want drop-in ready-to-go table output for Hashes,
|
27
27
|
Arrays and ActiveRecord objects.
|
28
28
|
|
29
|
-
|
30
|
-
but don't seem to specialize in what I want
|
29
|
+
Here's a quick list of other gems that I've tried out that are awesome and do much more than what Tablesmith does,
|
30
|
+
but don't seem to specialize in what I want:
|
31
31
|
|
32
32
|
- Hirb
|
33
33
|
- text-table
|
data/lib/tablesmith.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
|
-
require 'tablesmith/
|
2
|
-
require 'tablesmith/active_record_source'
|
1
|
+
require 'tablesmith/table'
|
3
2
|
require 'tablesmith/array_rows_source'
|
3
|
+
require 'tablesmith/hash_rows_base'
|
4
4
|
require 'tablesmith/hash_rows_source'
|
5
|
+
require 'tablesmith/active_record_source'
|
5
6
|
require 'tablesmith/version'
|
@@ -1,13 +1,11 @@
|
|
1
1
|
module Tablesmith::ActiveRecordSource
|
2
|
+
include Tablesmith::HashRowsBase
|
3
|
+
|
2
4
|
def convert_item_to_hash_row(item)
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
flatten_inner_hashes(hash)
|
8
|
-
else
|
9
|
-
super
|
10
|
-
end
|
5
|
+
item.reload unless item.new_record?
|
6
|
+
hash = item.serializable_hash(process_all_columns(serializable_options))
|
7
|
+
hash = fold_un_sourced_attributes_into_source_hash(first.class.name.underscore.to_sym, hash)
|
8
|
+
flatten_inner_hashes(hash)
|
11
9
|
end
|
12
10
|
|
13
11
|
def column_order
|
@@ -19,25 +17,30 @@ module Tablesmith::ActiveRecordSource
|
|
19
17
|
{}
|
20
18
|
end
|
21
19
|
|
22
|
-
# TODO: memoize
|
23
20
|
def process_all_columns(serializable_options)
|
24
|
-
|
21
|
+
build_columns(serializable_options)
|
25
22
|
|
26
|
-
|
23
|
+
serializable_options
|
24
|
+
end
|
27
25
|
|
28
|
-
|
26
|
+
def build_columns(serializable_options)
|
27
|
+
@columns || begin
|
28
|
+
@columns = []
|
29
29
|
|
30
|
-
|
31
|
-
unless include.is_a?(Hash)
|
32
|
-
include = Hash[Array.wrap(include).map { |n| n.is_a?(Hash) ? n.to_a.first : [n, {}] }]
|
33
|
-
end
|
30
|
+
process_columns(serializable_options, first.class)
|
34
31
|
|
35
|
-
|
36
|
-
ar_class = first.class.reflections[association].klass
|
37
|
-
process_columns(opts, ar_class)
|
38
|
-
end
|
32
|
+
include = serializable_options[:include]
|
39
33
|
|
40
|
-
|
34
|
+
# swiped from activemodel-3.2.17/lib/active_model/serialization.rb
|
35
|
+
unless include.is_a?(Hash)
|
36
|
+
include = Hash[Array.wrap(include).map { |n| n.is_a?(Hash) ? n.to_a.first : [n, {}] }]
|
37
|
+
end
|
38
|
+
|
39
|
+
include.each do |association, opts|
|
40
|
+
ar_class = first.class.reflections[association].klass
|
41
|
+
process_columns(opts, ar_class)
|
42
|
+
end
|
43
|
+
end
|
41
44
|
end
|
42
45
|
|
43
46
|
def process_columns(serializable_options, ar_class)
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# ActiveRecord and HashRowsSource share a lot, but not everything.
|
2
|
+
module Tablesmith::HashRowsBase
|
3
|
+
# not all resulting rows will have data in all columns, so make sure all rows pad out missing columns
|
4
|
+
def normalize_keys(rows)
|
5
|
+
all_keys = rows.map(&:keys).flatten.uniq
|
6
|
+
rows.map { |hash_row| all_keys.each { |key| hash_row[key] ||= '' } }
|
7
|
+
end
|
8
|
+
|
9
|
+
def sort_columns(rows)
|
10
|
+
rows.map! do |row|
|
11
|
+
# this sort gives preference to column_order then falls back to alphabetic for leftovers.
|
12
|
+
# this is handy when columns auto-generate based on hash data.
|
13
|
+
row.sort do |a, b|
|
14
|
+
a_col_name, b_col_name = [a.first, b.first]
|
15
|
+
a_col_index, b_col_index = [column_order.index(a_col_name), column_order.index(b_col_name)]
|
16
|
+
|
17
|
+
if a_col_index.nil? && b_col_index.nil?
|
18
|
+
a_col_name <=> b_col_name
|
19
|
+
else
|
20
|
+
(a_col_index || 999) <=> (b_col_index || 999)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def row_values(row)
|
27
|
+
row.map(&:last)
|
28
|
+
end
|
29
|
+
|
30
|
+
def create_headers(rows)
|
31
|
+
column_names = rows.first.map(&:first)
|
32
|
+
grouped_headers(column_names) + [apply_column_aliases(column_names), :separator]
|
33
|
+
end
|
34
|
+
end
|
@@ -1,4 +1,6 @@
|
|
1
1
|
module Tablesmith::HashRowsSource
|
2
|
+
include Tablesmith::HashRowsBase
|
3
|
+
|
2
4
|
def text_table
|
3
5
|
build_columns if columns.nil?
|
4
6
|
super
|
@@ -60,36 +62,4 @@ module Tablesmith::HashRowsSource
|
|
60
62
|
# Array addition from text-table
|
61
63
|
table.to_table(:first_row_is_head => true)
|
62
64
|
end
|
63
|
-
|
64
|
-
# not all resulting rows will have data in all columns, so make sure all rows pad out missing columns
|
65
|
-
def normalize_keys(rows)
|
66
|
-
all_keys = rows.map { |hash_row| hash_row.keys }.flatten.uniq
|
67
|
-
rows.map { |hash_row| all_keys.each { |key| hash_row[key] ||= '' } }
|
68
|
-
end
|
69
|
-
|
70
|
-
def sort_columns(rows)
|
71
|
-
rows.map! do |row|
|
72
|
-
# this sort gives preference to column_order then falls back to alphabetic for leftovers.
|
73
|
-
# this is handy when columns auto-generate based on hash data.
|
74
|
-
row.sort do |a, b|
|
75
|
-
a_col_name, b_col_name = [a.first, b.first]
|
76
|
-
a_col_index, b_col_index = [column_order.index(a_col_name), column_order.index(b_col_name)]
|
77
|
-
|
78
|
-
if a_col_index.nil? && b_col_index.nil?
|
79
|
-
a_col_name <=> b_col_name
|
80
|
-
else
|
81
|
-
(a_col_index || 999) <=> (b_col_index || 999)
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
def row_values(row)
|
88
|
-
row.map(&:last)
|
89
|
-
end
|
90
|
-
|
91
|
-
def create_headers(rows)
|
92
|
-
column_names = rows.first.map(&:first)
|
93
|
-
grouped_headers(column_names) + [apply_column_aliases(column_names), :separator]
|
94
|
-
end
|
95
65
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'text-table'
|
2
2
|
|
3
3
|
module Tablesmith
|
4
|
-
class
|
4
|
+
class Table < Array
|
5
5
|
def method_missing(meth_id, *args)
|
6
6
|
count = 1
|
7
7
|
self.map do |t|
|
@@ -62,7 +62,6 @@ module Tablesmith
|
|
62
62
|
[]
|
63
63
|
end
|
64
64
|
|
65
|
-
# TODO: resolve with column_order
|
66
65
|
def columns
|
67
66
|
@columns
|
68
67
|
end
|
@@ -118,12 +117,16 @@ module Tablesmith
|
|
118
117
|
def full_unaliased_name
|
119
118
|
"#{@source ? "#{@source}." : ''}#{@name}"
|
120
119
|
end
|
120
|
+
|
121
|
+
def to_s
|
122
|
+
"#{@source}.#{@name}#{' as ' + @alias if @alias}"
|
123
|
+
end
|
121
124
|
end
|
122
125
|
end
|
123
126
|
|
124
127
|
class Array
|
125
|
-
def
|
126
|
-
b = Tablesmith::
|
128
|
+
def to_table
|
129
|
+
b = Tablesmith::Table.new(self)
|
127
130
|
|
128
131
|
if defined?(ActiveRecord) && defined?(ActiveRecord::Base)
|
129
132
|
if b.first && b.first.is_a?(ActiveRecord::Base)
|
@@ -143,3 +146,9 @@ class Array
|
|
143
146
|
end
|
144
147
|
end
|
145
148
|
|
149
|
+
class Hash
|
150
|
+
def to_table
|
151
|
+
b = Tablesmith::Table.new([self])
|
152
|
+
b.extend Tablesmith::HashRowsSource
|
153
|
+
end
|
154
|
+
end
|
data/lib/tablesmith/version.rb
CHANGED
@@ -4,40 +4,40 @@ describe 'ActiveRecordSource' do
|
|
4
4
|
it 'outputs text table of multiple ActiveRecords' do
|
5
5
|
a = Person.new.tap { |c| c.first_name = 'A' }
|
6
6
|
b = Person.new.tap { |c| c.first_name = 'B' }
|
7
|
-
expected =
|
8
|
-
+----+------------+-----------+-----+-------------------+
|
9
|
-
| id | first_name | last_name | age | custom_attributes |
|
10
|
-
+----+------------+-----------+-----+-------------------+
|
11
|
-
| | A | | | |
|
12
|
-
| | B | | | |
|
13
|
-
+----+------------+-----------+-----+-------------------+
|
7
|
+
expected = <<~TABLE
|
8
|
+
+----+------------+-----------+-----+-------------------+
|
9
|
+
| id | first_name | last_name | age | custom_attributes |
|
10
|
+
+----+------------+-----------+-----+-------------------+
|
11
|
+
| | A | | | |
|
12
|
+
| | B | | | |
|
13
|
+
+----+------------+-----------+-----+-------------------+
|
14
14
|
TABLE
|
15
|
-
[a, b].
|
15
|
+
[a, b].to_table.text_table.to_s.should == expected
|
16
16
|
end
|
17
17
|
|
18
18
|
it 'outputs ActiveRecord in column order' do
|
19
19
|
p = Person.create(:first_name => 'chris', :last_name => 'mo', :age => 43)
|
20
|
-
expected =
|
21
|
-
+----+------------+-----------+-----+-------------------+
|
22
|
-
| id | first_name | last_name | age | custom_attributes |
|
23
|
-
+----+------------+-----------+-----+-------------------+
|
24
|
-
| 1 | chris | mo | 43 | |
|
25
|
-
+----+------------+-----------+-----+-------------------+
|
20
|
+
expected = <<~TABLE
|
21
|
+
+----+------------+-----------+-----+-------------------+
|
22
|
+
| id | first_name | last_name | age | custom_attributes |
|
23
|
+
+----+------------+-----------+-----+-------------------+
|
24
|
+
| 1 | chris | mo | 43 | |
|
25
|
+
+----+------------+-----------+-----+-------------------+
|
26
26
|
TABLE
|
27
|
-
[p].
|
27
|
+
[p].to_table.text_table.to_s.should == expected
|
28
28
|
end
|
29
29
|
|
30
30
|
it 'handles custom serialization options in batch' do
|
31
31
|
p = Person.create(:first_name => 'chrismo', :age => 43)
|
32
32
|
|
33
|
-
expected =
|
34
|
-
+------------+-----+-----------+
|
35
|
-
| first_name | age | year_born |
|
36
|
-
+------------+-----+-----------+
|
37
|
-
| chrismo | 43 | 1971 |
|
38
|
-
+------------+-----+-----------+
|
33
|
+
expected = <<~TABLE
|
34
|
+
+------------+-----+-----------+
|
35
|
+
| first_name | age | year_born |
|
36
|
+
+------------+-----+-----------+
|
37
|
+
| chrismo | 43 | 1971 |
|
38
|
+
+------------+-----+-----------+
|
39
39
|
TABLE
|
40
|
-
b = [p].
|
40
|
+
b = [p].to_table
|
41
41
|
|
42
42
|
def b.serializable_options
|
43
43
|
{:only => [:first_name, :age], :methods => [:year_born]}
|
@@ -46,16 +46,46 @@ describe 'ActiveRecordSource' do
|
|
46
46
|
b.text_table.to_s.should == expected
|
47
47
|
end
|
48
48
|
|
49
|
+
it 'auto reloads records' do
|
50
|
+
p = Person.create(:first_name => 'chrismo', :age => 43)
|
51
|
+
|
52
|
+
expected = <<~TABLE
|
53
|
+
+------------+-----+-----------+
|
54
|
+
| first_name | age | year_born |
|
55
|
+
+------------+-----+-----------+
|
56
|
+
| chrismo | 43 | 1971 |
|
57
|
+
+------------+-----+-----------+
|
58
|
+
TABLE
|
59
|
+
b = [p].to_table
|
60
|
+
|
61
|
+
def b.serializable_options
|
62
|
+
{:only => [:first_name, :age], :methods => [:year_born]}
|
63
|
+
end
|
64
|
+
b.text_table.to_s.should == expected
|
65
|
+
|
66
|
+
# update the value through another instance.
|
67
|
+
Person.last.update_column(:age, 46)
|
68
|
+
|
69
|
+
expected = <<~TABLE
|
70
|
+
+------------+-----+-----------+
|
71
|
+
| first_name | age | year_born |
|
72
|
+
+------------+-----+-----------+
|
73
|
+
| chrismo | 46 | 1968 |
|
74
|
+
+------------+-----+-----------+
|
75
|
+
TABLE
|
76
|
+
b.text_table.to_s.should == expected
|
77
|
+
end
|
78
|
+
|
49
79
|
it 'handles column name partials' do
|
50
80
|
p = Person.create(:first_name => 'chris', :last_name => 'mo', :age => 43)
|
51
|
-
expected =
|
52
|
-
+-------+------+-----+
|
53
|
-
| first | last | age |
|
54
|
-
+-------+------+-----+
|
55
|
-
| chris | mo | 43 |
|
56
|
-
+-------+------+-----+
|
81
|
+
expected = <<~TABLE
|
82
|
+
+-------+------+-----+
|
83
|
+
| first | last | age |
|
84
|
+
+-------+------+-----+
|
85
|
+
| chris | mo | 43 |
|
86
|
+
+-------+------+-----+
|
57
87
|
TABLE
|
58
|
-
b = [p].
|
88
|
+
b = [p].to_table
|
59
89
|
|
60
90
|
def b.serializable_options
|
61
91
|
{:only => [:first, :last, :age]}
|
@@ -66,14 +96,14 @@ describe 'ActiveRecordSource' do
|
|
66
96
|
|
67
97
|
it 'handles column name partials across words' do
|
68
98
|
p = Person.create(:first_name => 'chris', :last_name => 'mo', :age => 43)
|
69
|
-
expected =
|
70
|
-
+--------+--------+-----+
|
71
|
-
| f_name | l_name | age |
|
72
|
-
+--------+--------+-----+
|
73
|
-
| chris | mo | 43 |
|
74
|
-
+--------+--------+-----+
|
99
|
+
expected = <<~TABLE
|
100
|
+
+--------+--------+-----+
|
101
|
+
| f_name | l_name | age |
|
102
|
+
+--------+--------+-----+
|
103
|
+
| chris | mo | 43 |
|
104
|
+
+--------+--------+-----+
|
75
105
|
TABLE
|
76
|
-
b = [p].
|
106
|
+
b = [p].to_table
|
77
107
|
|
78
108
|
def b.serializable_options
|
79
109
|
{:only => [:f_name, :l_name, :age]}
|
@@ -84,14 +114,14 @@ describe 'ActiveRecordSource' do
|
|
84
114
|
|
85
115
|
it 'handles explicit column aliases' do
|
86
116
|
p = Person.create(:first_name => 'chris', :last_name => 'mo', :age => 43)
|
87
|
-
expected =
|
88
|
-
+---------------+----------+-----+
|
89
|
-
| primer_nombre | apellido | age |
|
90
|
-
+---------------+----------+-----+
|
91
|
-
| chris | mo | 43 |
|
92
|
-
+---------------+----------+-----+
|
117
|
+
expected = <<~TABLE
|
118
|
+
+---------------+----------+-----+
|
119
|
+
| primer_nombre | apellido | age |
|
120
|
+
+---------------+----------+-----+
|
121
|
+
| chris | mo | 43 |
|
122
|
+
+---------------+----------+-----+
|
93
123
|
TABLE
|
94
|
-
b = [p].
|
124
|
+
b = [p].to_table
|
95
125
|
|
96
126
|
def b.columns
|
97
127
|
[Tablesmith::Column.new(name: :first_name, alias: :primer_nombre),
|
@@ -108,20 +138,20 @@ describe 'ActiveRecordSource' do
|
|
108
138
|
it 'handles associations without aliases' do
|
109
139
|
s = Supplier.create(name: 'supplier')
|
110
140
|
s.account = Account.create(name: 'account', tax_identification_number: '123456')
|
111
|
-
b = [s].
|
141
|
+
b = [s].to_table
|
112
142
|
|
113
143
|
def b.serializable_options
|
114
144
|
{:only => [:name], :include => {:account => {:only => [:name, :tax_identification_number]}}}
|
115
145
|
end
|
116
146
|
|
117
|
-
expected =
|
118
|
-
+----------+---------+---------------------------+
|
119
|
-
| supplier | account |
|
120
|
-
+----------+---------+---------------------------+
|
121
|
-
| name | name | tax_identification_number |
|
122
|
-
+----------+---------+---------------------------+
|
123
|
-
| supplier | account | 123456 |
|
124
|
-
+----------+---------+---------------------------+
|
147
|
+
expected = <<~TABLE
|
148
|
+
+----------+---------+---------------------------+
|
149
|
+
| supplier | account |
|
150
|
+
+----------+---------+---------------------------+
|
151
|
+
| name | name | tax_identification_number |
|
152
|
+
+----------+---------+---------------------------+
|
153
|
+
| supplier | account | 123456 |
|
154
|
+
+----------+---------+---------------------------+
|
125
155
|
TABLE
|
126
156
|
|
127
157
|
b.text_table.to_s.should == expected
|
@@ -130,20 +160,20 @@ describe 'ActiveRecordSource' do
|
|
130
160
|
it 'handles associations with aliases' do
|
131
161
|
s = Supplier.create(name: 'supplier')
|
132
162
|
s.account = Account.create(name: 'account', tax_identification_number: '123456')
|
133
|
-
b = [s].
|
163
|
+
b = [s].to_table
|
134
164
|
|
135
165
|
def b.serializable_options
|
136
166
|
{:only => [:name], :include => {:account => {:only => [:name, :tax_id]}}}
|
137
167
|
end
|
138
168
|
|
139
|
-
expected =
|
140
|
-
+----------+---------+--------+
|
141
|
-
| supplier | account |
|
142
|
-
+----------+---------+--------+
|
143
|
-
| name | name | tax_id |
|
144
|
-
+----------+---------+--------+
|
145
|
-
| supplier | account | 123456 |
|
146
|
-
+----------+---------+--------+
|
169
|
+
expected = <<~TABLE
|
170
|
+
+----------+---------+--------+
|
171
|
+
| supplier | account |
|
172
|
+
+----------+---------+--------+
|
173
|
+
| name | name | tax_id |
|
174
|
+
+----------+---------+--------+
|
175
|
+
| supplier | account | 123456 |
|
176
|
+
+----------+---------+--------+
|
147
177
|
TABLE
|
148
178
|
|
149
179
|
b.text_table.to_s.should == expected
|
@@ -158,17 +188,17 @@ describe 'ActiveRecordSource' do
|
|
158
188
|
# may need/want to handle the hash resulting from an association differently from the hash resulting from a method/attr
|
159
189
|
it 'supports field with hash contents' do
|
160
190
|
p = Person.create(first_name: 'chrismo', custom_attributes: {skills: {instrument: 'piano', style: 'jazz'}})
|
161
|
-
b = [p].
|
191
|
+
b = [p].to_table
|
162
192
|
|
163
193
|
a = format_ids([p.id])[0]
|
164
|
-
expected =
|
165
|
-
+----+------------+-----------+-----+----------------------------------------+
|
166
|
-
| person | custom_attributes |
|
167
|
-
+----+------------+-----------+-----+----------------------------------------+
|
168
|
-
| id | first_name | last_name | age | skills |
|
169
|
-
+----+------------+-----------+-----+----------------------------------------+
|
170
|
-
|#{a}| chrismo | | | {:instrument=>"piano", :style=>"jazz"} |
|
171
|
-
+----+------------+-----------+-----+----------------------------------------+
|
194
|
+
expected = <<~TABLE
|
195
|
+
+----+------------+-----------+-----+----------------------------------------+
|
196
|
+
| person | custom_attributes |
|
197
|
+
+----+------------+-----------+-----+----------------------------------------+
|
198
|
+
| id | first_name | last_name | age | skills |
|
199
|
+
+----+------------+-----------+-----+----------------------------------------+
|
200
|
+
|#{a}| chrismo | | | {:instrument=>"piano", :style=>"jazz"} |
|
201
|
+
+----+------------+-----------+-----+----------------------------------------+
|
172
202
|
TABLE
|
173
203
|
|
174
204
|
b.text_table.to_s.should == expected
|
@@ -178,20 +208,20 @@ describe 'ActiveRecordSource' do
|
|
178
208
|
p2 = Person.create(first_name: 'romer', custom_attributes: {instrument: 'kazoo'})
|
179
209
|
p1 = Person.create(first_name: 'chrismo', custom_attributes: {instrument: 'piano', style: 'jazz'})
|
180
210
|
p3 = Person.create(first_name: 'glv', custom_attributes: {})
|
181
|
-
batch = [p2, p1, p3].
|
211
|
+
batch = [p2, p1, p3].to_table
|
182
212
|
|
183
213
|
a, b, c = format_ids([p2.id, p1.id, p3.id])
|
184
214
|
|
185
|
-
expected =
|
186
|
-
+----+------------+-----------+-----+------------+-----------+
|
187
|
-
| person | custom_attributes |
|
188
|
-
+----+------------+-----------+-----+------------+-----------+
|
189
|
-
| id | first_name | last_name | age | instrument | style |
|
190
|
-
+----+------------+-----------+-----+------------+-----------+
|
191
|
-
|#{a}| romer | | | kazoo | |
|
192
|
-
|#{b}| chrismo | | | piano | jazz |
|
193
|
-
|#{c}| glv | | | | |
|
194
|
-
+----+------------+-----------+-----+------------+-----------+
|
215
|
+
expected = <<~TABLE
|
216
|
+
+----+------------+-----------+-----+------------+-----------+
|
217
|
+
| person | custom_attributes |
|
218
|
+
+----+------------+-----------+-----+------------+-----------+
|
219
|
+
| id | first_name | last_name | age | instrument | style |
|
220
|
+
+----+------------+-----------+-----+------------+-----------+
|
221
|
+
|#{a}| romer | | | kazoo | |
|
222
|
+
|#{b}| chrismo | | | piano | jazz |
|
223
|
+
|#{c}| glv | | | | |
|
224
|
+
+----+------------+-----------+-----+------------+-----------+
|
195
225
|
TABLE
|
196
226
|
|
197
227
|
batch.text_table.to_s.should == expected
|
@@ -200,19 +230,19 @@ describe 'ActiveRecordSource' do
|
|
200
230
|
it 'supports consistent ordering of dynamic columns' do
|
201
231
|
p1 = Person.create(first_name: 'chrismo', custom_attributes: {instrument: 'piano', style: 'jazz'})
|
202
232
|
p2 = Person.create(first_name: 'romer', custom_attributes: {hobby: 'games'})
|
203
|
-
batch = [p1, p2].
|
233
|
+
batch = [p1, p2].to_table
|
204
234
|
|
205
235
|
a, b = format_ids([p1.id, p2.id])
|
206
236
|
|
207
|
-
expected =
|
208
|
-
+----+------------+-----------+-----+--------+------------+--------+
|
209
|
-
| person | custom_attributes |
|
210
|
-
+----+------------+-----------+-----+--------+------------+--------+
|
211
|
-
| id | first_name | last_name | age | hobby | instrument | style |
|
212
|
-
+----+------------+-----------+-----+--------+------------+--------+
|
213
|
-
|#{a}| chrismo | | | | piano | jazz |
|
214
|
-
|#{b}| romer | | | games | | |
|
215
|
-
+----+------------+-----------+-----+--------+------------+--------+
|
237
|
+
expected = <<~TABLE
|
238
|
+
+----+------------+-----------+-----+--------+------------+--------+
|
239
|
+
| person | custom_attributes |
|
240
|
+
+----+------------+-----------+-----+--------+------------+--------+
|
241
|
+
| id | first_name | last_name | age | hobby | instrument | style |
|
242
|
+
+----+------------+-----------+-----+--------+------------+--------+
|
243
|
+
|#{a}| chrismo | | | | piano | jazz |
|
244
|
+
|#{b}| romer | | | games | | |
|
245
|
+
+----+------------+-----------+-----+--------+------------+--------+
|
216
246
|
TABLE
|
217
247
|
|
218
248
|
batch.text_table.to_s.should == expected
|
@@ -220,18 +250,18 @@ describe 'ActiveRecordSource' do
|
|
220
250
|
|
221
251
|
it 'handles AR instance without an association present' do
|
222
252
|
s = Supplier.create(name: 'supplier')
|
223
|
-
b = [s].
|
253
|
+
b = [s].to_table
|
224
254
|
|
225
255
|
def b.serializable_options
|
226
256
|
{:only => [:name], :include => {:account => {:only => [:name, :tax_id]}}}
|
227
257
|
end
|
228
258
|
|
229
|
-
expected =
|
230
|
-
+----------+
|
231
|
-
| name |
|
232
|
-
+----------+
|
233
|
-
| supplier |
|
234
|
-
+----------+
|
259
|
+
expected = <<~TABLE
|
260
|
+
+----------+
|
261
|
+
| name |
|
262
|
+
+----------+
|
263
|
+
| supplier |
|
264
|
+
+----------+
|
235
265
|
TABLE
|
236
266
|
|
237
267
|
b.text_table.to_s.should == expected
|
@@ -244,21 +274,21 @@ describe 'ActiveRecordSource' do
|
|
244
274
|
''
|
245
275
|
end
|
246
276
|
|
247
|
-
b = [s2].
|
277
|
+
b = [s2].to_table
|
248
278
|
|
249
279
|
# methods need Columns as well
|
250
280
|
def b.serializable_options
|
251
281
|
{:only => [:name, :custom_attributes], :methods => [:foo]}
|
252
282
|
end
|
253
283
|
|
254
|
-
expected =
|
255
|
-
+----------+------+-------------------+
|
256
|
-
| supplier | custom_attributes |
|
257
|
-
+----------+------+-------------------+
|
258
|
-
| name | foo | a |
|
259
|
-
+----------+------+-------------------+
|
260
|
-
| sup. two | | 1 |
|
261
|
-
+----------+------+-------------------+
|
284
|
+
expected = <<~TABLE
|
285
|
+
+----------+------+-------------------+
|
286
|
+
| supplier | custom_attributes |
|
287
|
+
+----------+------+-------------------+
|
288
|
+
| name | foo | a |
|
289
|
+
+----------+------+-------------------+
|
290
|
+
| sup. two | | 1 |
|
291
|
+
+----------+------+-------------------+
|
262
292
|
TABLE
|
263
293
|
|
264
294
|
b.text_table.to_s.should == expected
|
@@ -268,15 +298,15 @@ describe 'ActiveRecordSource' do
|
|
268
298
|
p = Parent.create(name: 'parent')
|
269
299
|
c = Child.create(name: 'child', parent: p)
|
270
300
|
|
271
|
-
b = [p].
|
301
|
+
b = [p].to_table
|
272
302
|
|
273
303
|
# little weird looking at this point, but at least not broken
|
274
|
-
expected =
|
275
|
-
+----+--------+-------------------+---------------------+
|
276
|
-
| id | name | custom_attributes | children |
|
277
|
-
+----+--------+-------------------+---------------------+
|
278
|
-
| 1 | parent | | [{"name"=>"child"}] |
|
279
|
-
+----+--------+-------------------+---------------------+
|
304
|
+
expected = <<~TABLE
|
305
|
+
+----+--------+-------------------+---------------------+
|
306
|
+
| id | name | custom_attributes | children |
|
307
|
+
+----+--------+-------------------+---------------------+
|
308
|
+
| 1 | parent | | [{"name"=>"child"}] |
|
309
|
+
+----+--------+-------------------+---------------------+
|
280
310
|
TABLE
|
281
311
|
|
282
312
|
def b.serializable_options
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Array Source' do
|
4
|
+
it 'just works in a console' do
|
5
|
+
expected = <<~TABLE
|
6
|
+
+---+---+---+
|
7
|
+
| a | b | c |
|
8
|
+
+---+---+---+
|
9
|
+
| d | e | f |
|
10
|
+
+---+---+---+
|
11
|
+
TABLE
|
12
|
+
[%w(a b c), %w(d e f)].to_table.text_table.to_s.should == expected
|
13
|
+
end
|
14
|
+
end
|
data/spec/fixtures.rb
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
require 'active_record'
|
2
2
|
|
3
|
-
ActiveRecord::Base.establish_connection :
|
3
|
+
ActiveRecord::Base.establish_connection adapter: 'sqlite3', database: ':memory:'
|
4
4
|
|
5
5
|
class Person < ActiveRecord::Base
|
6
|
-
|
6
|
+
serialize :custom_attributes
|
7
|
+
|
8
|
+
connection.create_table table_name, force: true do |t|
|
7
9
|
t.string :first_name
|
8
10
|
t.string :last_name
|
9
11
|
t.integer :age
|
@@ -11,14 +13,14 @@ class Person < ActiveRecord::Base
|
|
11
13
|
end
|
12
14
|
|
13
15
|
def year_born
|
14
|
-
Time.local(2014, 1, 1).year -
|
16
|
+
Time.local(2014, 1, 1).year - age
|
15
17
|
end
|
16
18
|
end
|
17
19
|
|
18
20
|
class Parent < ActiveRecord::Base
|
19
21
|
has_many :children
|
20
22
|
|
21
|
-
connection.create_table table_name, :
|
23
|
+
connection.create_table table_name, force: true do |t|
|
22
24
|
t.string :name
|
23
25
|
t.text :custom_attributes
|
24
26
|
end
|
@@ -27,7 +29,7 @@ end
|
|
27
29
|
class Child < ActiveRecord::Base
|
28
30
|
belongs_to :parent
|
29
31
|
|
30
|
-
connection.create_table table_name, :
|
32
|
+
connection.create_table table_name, force: true do |t|
|
31
33
|
t.integer :parent_id
|
32
34
|
t.string :name
|
33
35
|
end
|
@@ -35,11 +37,13 @@ end
|
|
35
37
|
|
36
38
|
class Supplier < ActiveRecord::Base
|
37
39
|
has_one :account
|
38
|
-
has_one :account_history, :
|
40
|
+
has_one :account_history, through: :account
|
39
41
|
|
40
42
|
accepts_nested_attributes_for :account, :account_history
|
41
43
|
|
42
|
-
|
44
|
+
serialize :custom_attributes
|
45
|
+
|
46
|
+
connection.create_table table_name, force: true do |t|
|
43
47
|
t.integer :account_id
|
44
48
|
t.integer :account_history_id
|
45
49
|
t.string :name
|
@@ -53,7 +57,7 @@ class Account < ActiveRecord::Base
|
|
53
57
|
|
54
58
|
accepts_nested_attributes_for :account_history
|
55
59
|
|
56
|
-
connection.create_table table_name, :
|
60
|
+
connection.create_table table_name, force: true do |t|
|
57
61
|
t.integer :supplier_id
|
58
62
|
t.string :name
|
59
63
|
t.integer :tax_identification_number
|
@@ -63,7 +67,7 @@ end
|
|
63
67
|
class AccountHistory < ActiveRecord::Base
|
64
68
|
belongs_to :account
|
65
69
|
|
66
|
-
connection.create_table table_name, :
|
70
|
+
connection.create_table table_name, force: true do |t|
|
67
71
|
t.integer :account_id
|
68
72
|
t.integer :credit_rating
|
69
73
|
end
|
@@ -2,42 +2,42 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe 'HashRowsSource' do
|
4
4
|
it 'outputs text table of simple hash row with default columns' do
|
5
|
-
expected =
|
6
|
-
+---+---+
|
7
|
-
| a | b |
|
8
|
-
+---+---+
|
9
|
-
| 1 | 2 |
|
10
|
-
+---+---+
|
5
|
+
expected = <<~TABLE
|
6
|
+
+---+---+
|
7
|
+
| a | b |
|
8
|
+
+---+---+
|
9
|
+
| 1 | 2 |
|
10
|
+
+---+---+
|
11
11
|
TABLE
|
12
|
-
[{a: 1, b: 2}].
|
12
|
+
[{a: 1, b: 2}].to_table.text_table.to_s.should == expected
|
13
13
|
end
|
14
14
|
|
15
15
|
it 'outputs text table of mixed columns hash rows with default columns' do
|
16
|
-
expected =
|
17
|
-
+---+---+---+
|
18
|
-
| a | b | c |
|
19
|
-
+---+---+---+
|
20
|
-
| 1 | 2 | |
|
21
|
-
| 2 | | ! |
|
22
|
-
+---+---+---+
|
16
|
+
expected = <<~TABLE
|
17
|
+
+---+---+---+
|
18
|
+
| a | b | c |
|
19
|
+
+---+---+---+
|
20
|
+
| 1 | 2 | |
|
21
|
+
| 2 | | ! |
|
22
|
+
+---+---+---+
|
23
23
|
TABLE
|
24
24
|
[
|
25
25
|
{a: 1, b: 2},
|
26
26
|
{a: 2, c: '!'}
|
27
|
-
].
|
27
|
+
].to_table.text_table.to_s.should == expected
|
28
28
|
end
|
29
29
|
|
30
30
|
it 'outputs text table of deep hash rows with defined columns' do
|
31
|
-
expected =
|
32
|
-
+---+---+---+
|
33
|
-
| | b |
|
34
|
-
+---+---+---+
|
35
|
-
| a | c | d |
|
36
|
-
+---+---+---+
|
37
|
-
| 1 | 2 | 2 |
|
38
|
-
+---+---+---+
|
31
|
+
expected = <<~TABLE
|
32
|
+
+---+---+---+
|
33
|
+
| | b |
|
34
|
+
+---+---+---+
|
35
|
+
| a | c | d |
|
36
|
+
+---+---+---+
|
37
|
+
| 1 | 2 | 2 |
|
38
|
+
+---+---+---+
|
39
39
|
TABLE
|
40
|
-
b = [{a: 1, b: {c: 2, d: 2}}].
|
40
|
+
b = [{a: 1, b: {c: 2, d: 2}}].to_table
|
41
41
|
def b.columns
|
42
42
|
[
|
43
43
|
Column.new(name: :a),
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Hash Source' do
|
4
|
+
it 'just works in a console' do
|
5
|
+
expected = <<~TABLE
|
6
|
+
+---+---+---+
|
7
|
+
| a | b | c |
|
8
|
+
+---+---+---+
|
9
|
+
| 1 | 2 | 3 |
|
10
|
+
+---+---+---+
|
11
|
+
TABLE
|
12
|
+
{a: 1, b: 2, c: 3}.to_table.text_table.to_s.should == expected
|
13
|
+
end
|
14
|
+
end
|
@@ -2,19 +2,19 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
include Tablesmith
|
4
4
|
|
5
|
-
describe
|
5
|
+
describe Table do
|
6
6
|
it 'should subclass array' do
|
7
|
-
b =
|
7
|
+
b = Table.new
|
8
8
|
b.length.should == 0
|
9
9
|
b << 1
|
10
10
|
b << 'a'
|
11
11
|
b[0].should == 1
|
12
12
|
b[1].should == 'a'
|
13
|
-
b.class.should ==
|
13
|
+
b.class.should == Table
|
14
14
|
end
|
15
15
|
|
16
16
|
it 'should pass unmatched Array messages to all items' do
|
17
|
-
b =
|
17
|
+
b = Table.new
|
18
18
|
b.length.should == 0
|
19
19
|
b << 1
|
20
20
|
b << '2'
|
@@ -22,24 +22,24 @@ describe Batch do
|
|
22
22
|
end
|
23
23
|
|
24
24
|
it 'should handle empty Array' do
|
25
|
-
expected =
|
26
|
-
+---------+
|
27
|
-
| (empty) |
|
28
|
-
+---------+
|
25
|
+
expected = <<~TEXT
|
26
|
+
+---------+
|
27
|
+
| (empty) |
|
28
|
+
+---------+
|
29
29
|
TEXT
|
30
|
-
[].
|
30
|
+
[].to_table.text_table.to_s.should == expected
|
31
31
|
end
|
32
32
|
|
33
33
|
it 'should handle a simple two row Array' do
|
34
34
|
a = [%w(a b c), %w(d e f)]
|
35
35
|
actual = a
|
36
|
-
expected =
|
37
|
-
+---+---+---+
|
38
|
-
| a | b | c |
|
39
|
-
+---+---+---+
|
40
|
-
| d | e | f |
|
41
|
-
+---+---+---+
|
36
|
+
expected = <<~TABLE
|
37
|
+
+---+---+---+
|
38
|
+
| a | b | c |
|
39
|
+
+---+---+---+
|
40
|
+
| d | e | f |
|
41
|
+
+---+---+---+
|
42
42
|
TABLE
|
43
|
-
actual.
|
43
|
+
actual.to_table.text_table.to_s.should == expected
|
44
44
|
end
|
45
45
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tablesmith
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- chrismo
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-08-
|
11
|
+
date: 2017-08-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: text-table
|
@@ -117,7 +117,9 @@ extra_rdoc_files: []
|
|
117
117
|
files:
|
118
118
|
- ".gitignore"
|
119
119
|
- ".rspec"
|
120
|
+
- ".rubocop.yml"
|
120
121
|
- ".ruby-version"
|
122
|
+
- ".travis.yml"
|
121
123
|
- Gemfile
|
122
124
|
- Gemfile.lock
|
123
125
|
- LICENSE
|
@@ -126,16 +128,17 @@ files:
|
|
126
128
|
- lib/tablesmith.rb
|
127
129
|
- lib/tablesmith/active_record_source.rb
|
128
130
|
- lib/tablesmith/array_rows_source.rb
|
129
|
-
- lib/tablesmith/
|
131
|
+
- lib/tablesmith/hash_rows_base.rb
|
130
132
|
- lib/tablesmith/hash_rows_source.rb
|
133
|
+
- lib/tablesmith/table.rb
|
131
134
|
- lib/tablesmith/version.rb
|
132
|
-
- spec/
|
133
|
-
- spec/
|
134
|
-
- spec/batch_spec.rb
|
135
|
+
- spec/active_record_table_spec.rb
|
136
|
+
- spec/array_table_spec.rb
|
135
137
|
- spec/fixtures.rb
|
136
|
-
- spec/
|
137
|
-
- spec/
|
138
|
+
- spec/hash_rows_table_spec.rb
|
139
|
+
- spec/hash_table_spec.rb
|
138
140
|
- spec/spec_helper.rb
|
141
|
+
- spec/table_spec.rb
|
139
142
|
- tablesmith.gemspec
|
140
143
|
homepage: http://github.com/livingsocial/tablesmith
|
141
144
|
licenses: []
|
@@ -161,10 +164,10 @@ signing_key:
|
|
161
164
|
specification_version: 4
|
162
165
|
summary: Minimal console table
|
163
166
|
test_files:
|
164
|
-
- spec/
|
165
|
-
- spec/
|
166
|
-
- spec/batch_spec.rb
|
167
|
+
- spec/active_record_table_spec.rb
|
168
|
+
- spec/array_table_spec.rb
|
167
169
|
- spec/fixtures.rb
|
168
|
-
- spec/
|
169
|
-
- spec/
|
170
|
+
- spec/hash_rows_table_spec.rb
|
171
|
+
- spec/hash_table_spec.rb
|
170
172
|
- spec/spec_helper.rb
|
173
|
+
- spec/table_spec.rb
|
data/spec/array_batch_spec.rb
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe 'Array Source' do
|
4
|
-
it 'just works in a console' do
|
5
|
-
expected = <<-TABLE
|
6
|
-
+---+---+---+
|
7
|
-
| a | b | c |
|
8
|
-
+---+---+---+
|
9
|
-
| d | e | f |
|
10
|
-
+---+---+---+
|
11
|
-
TABLE
|
12
|
-
[%w(a b c), %w(d e f)].to_batch.text_table.to_s.should == expected
|
13
|
-
end
|
14
|
-
end
|
data/spec/hash_spec.rb
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
# Want stuff to work with a plain Hash
|