tablesmith 0.1.2 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|