tablesmith 0.4.1 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Tablesmith::HashRowsSource
2
4
  include Tablesmith::HashRowsBase
3
5
 
@@ -11,7 +13,7 @@ module Tablesmith::HashRowsSource
11
13
  end
12
14
 
13
15
  def flatten_hash_to_row(deep_hash, columns)
14
- row = ActiveSupport::OrderedHash.new
16
+ row = {}
15
17
  columns.each do |col_or_hash|
16
18
  value_from_hash(row, deep_hash, col_or_hash)
17
19
  end
@@ -21,9 +23,9 @@ module Tablesmith::HashRowsSource
21
23
  # TODO: no support for deep
22
24
  def build_columns
23
25
  @columns ||= []
24
- self.map do |hash_row|
26
+ map do |hash_row|
25
27
  @columns << hash_row.keys.map { |k| Tablesmith::Column.new(name: k) }
26
- end
28
+ end
27
29
  @columns.flatten!
28
30
  end
29
31
 
@@ -37,11 +39,9 @@ module Tablesmith::HashRowsSource
37
39
  value_from_hash(row, deep_hash[sub_hash_key], inner_col_or_hash)
38
40
  end
39
41
  end
40
- else
41
- nil
42
42
  end
43
- rescue => e
44
- $stderr.puts "#{e.message}: #{col_or_hash}" if @debug
43
+ rescue StandardError => e
44
+ warn "#{e.message}: #{col_or_hash}" if @debug
45
45
  end
46
46
 
47
47
  def hash_rows_to_text_table(hash_rows)
@@ -60,6 +60,6 @@ module Tablesmith::HashRowsSource
60
60
  end
61
61
 
62
62
  # Array addition from text-table
63
- table.to_table(:first_row_is_head => true)
63
+ table.to_table(first_row_is_head: true)
64
64
  end
65
65
  end
@@ -40,6 +40,7 @@ class HtmlFormatter
40
40
  def append_rows(rows, tag)
41
41
  rows.each do |row|
42
42
  next if row == :separator
43
+
43
44
  @lines << "#{indent}<tr>"
44
45
  row.map do |cell|
45
46
  value = cell_value(cell)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'text-table'
2
4
  require 'csv'
3
5
 
@@ -5,8 +7,8 @@ module Tablesmith
5
7
  class Table < Array
6
8
  def method_missing(meth_id, *args)
7
9
  count = 1
8
- self.map do |t|
9
- $stderr.print '.' if count.divmod(100)[1] == 0
10
+ map do |t|
11
+ $stderr.print '.' if (count.divmod(100)[1]).zero?
10
12
  count += 1
11
13
  t.send(meth_id, *args)
12
14
  end
@@ -16,6 +18,10 @@ module Tablesmith
16
18
  super
17
19
  end
18
20
 
21
+ def to_s
22
+ text_table.to_s
23
+ end
24
+
19
25
  # irb
20
26
  def inspect
21
27
  pretty_inspect
@@ -32,9 +38,9 @@ module Tablesmith
32
38
  end
33
39
 
34
40
  def text_table
35
- return ['(empty)'].to_text_table if self.empty?
41
+ return ['(empty)'].to_text_table if empty?
36
42
 
37
- rows = self.map { |item| convert_item_to_hash_row(item) }.compact
43
+ rows = map { |item| convert_item_to_hash_row(item) }.compact
38
44
 
39
45
  normalize_keys(rows)
40
46
 
@@ -48,12 +54,13 @@ module Tablesmith
48
54
  CSV.generate do |csv|
49
55
  text_table.rows.each do |row|
50
56
  next if row == :separator
57
+
51
58
  csv << row.map do |cell|
52
59
  case cell
53
- when Hash
54
- cell[:value]
55
- else
56
- cell
60
+ when Hash
61
+ cell[:value]
62
+ else
63
+ cell
57
64
  end
58
65
  end
59
66
  end
@@ -70,8 +77,7 @@ module Tablesmith
70
77
  end
71
78
 
72
79
  # override in subclass or mixin
73
- def sort_columns(rows)
74
- end
80
+ def sort_columns(rows); end
75
81
 
76
82
  # override in subclass or mixin
77
83
  def convert_item_to_hash_row(item)
@@ -79,22 +85,24 @@ module Tablesmith
79
85
  end
80
86
 
81
87
  # override in subclass or mixin
82
- def normalize_keys(rows)
83
- end
88
+ def normalize_keys(rows); end
84
89
 
85
90
  # override in subclass or mixin
86
91
  def column_order
87
92
  []
88
93
  end
89
94
 
90
- def columns
91
- @columns
92
- end
95
+ attr_reader :columns
93
96
 
94
97
  def create_headers(rows)
95
- top_row = rows.first
96
- column_names = top_row.first.is_a?(Array) ? top_row.map(&:first) : top_row
97
- grouped_headers(column_names) + [apply_column_aliases(column_names), :separator]
98
+ first_element = rows.first
99
+ if first_element.is_a?(Array)
100
+ top_row = first_element
101
+ column_names = top_row.first.is_a?(Array) ? top_row.map(&:first) : top_row
102
+ grouped_headers(column_names) + [apply_column_aliases(column_names), :separator]
103
+ else
104
+ []
105
+ end
98
106
  end
99
107
 
100
108
  def grouped_headers(column_names)
@@ -111,7 +119,7 @@ module Tablesmith
111
119
  row = []
112
120
  # this relies on Ruby versions where hash retains add order
113
121
  groups.each_pair do |name, span|
114
- row << {value: name, align: :center, colspan: span}
122
+ row << { value: name, align: :center, colspan: span }
115
123
  end
116
124
  [row, :separator]
117
125
  end
@@ -121,7 +129,7 @@ module Tablesmith
121
129
  column_names.map do |name|
122
130
  instance = columns.detect { |ca| ca.name.to_s == name.to_s }
123
131
  value = instance ? instance.display_name : name
124
- {:value => value, :align => :center}
132
+ { value: value, align: :center }
125
133
  end
126
134
  end
127
135
  end
@@ -129,14 +137,14 @@ module Tablesmith
129
137
  class Column
130
138
  attr_accessor :source, :name, :alias
131
139
 
132
- def initialize(attributes={})
140
+ def initialize(attributes = {})
133
141
  @source = attributes.delete(:source)
134
142
  @name = attributes.delete(:name)
135
143
  @alias = attributes.delete(:alias)
136
144
  end
137
145
 
138
146
  def display_name
139
- "#{@alias || @name}"
147
+ (@alias || @name).to_s
140
148
  end
141
149
 
142
150
  def full_unaliased_name
@@ -157,18 +165,12 @@ class Array
157
165
  # so mixed content could be supported. Maybe every cell could be
158
166
  # rendered appropriately, with nested tables.
159
167
  if defined?(ActiveRecord) && defined?(ActiveRecord::Base)
160
- if b.first && b.first.is_a?(ActiveRecord::Base)
161
- b.extend Tablesmith::ActiveRecordSource
162
- end
168
+ b.extend Tablesmith::ActiveRecordSource if b.first&.is_a?(ActiveRecord::Base)
163
169
  end
164
170
 
165
- if b.first && b.first.is_a?(Hash)
166
- b.extend Tablesmith::HashRowsSource
167
- end
171
+ b.extend Tablesmith::HashRowsSource if b.first&.is_a?(Hash)
168
172
 
169
- if b.first && b.first.is_a?(Array)
170
- b.extend Tablesmith::ArrayRowsSource
171
- end
173
+ b.extend Tablesmith::ArrayRowsSource if b.first&.is_a?(Array)
172
174
 
173
175
  b
174
176
  end
@@ -179,4 +181,4 @@ class Hash
179
181
  b = Tablesmith::Table.new([self])
180
182
  b.extend Tablesmith::HashRowsSource
181
183
  end
182
- end
184
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Tablesmith
2
- VERSION = '0.4.1'.freeze
4
+ VERSION = '0.5.0'
3
5
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  describe 'ActiveRecordSource' do
@@ -12,11 +14,11 @@ describe 'ActiveRecordSource' do
12
14
  | | B | | | |
13
15
  +----+------------+-----------+-----+-------------------+
14
16
  TABLE
15
- [a, b].to_table.text_table.to_s.should == expected
17
+ [a, b].to_table.to_s.should == expected
16
18
  end
17
19
 
18
20
  it 'outputs ActiveRecord in column order' do
19
- p = Person.create(:first_name => 'chris', :last_name => 'mo', :age => 43)
21
+ p = Person.create(first_name: 'chris', last_name: 'mo', age: 43)
20
22
  expected = <<~TABLE
21
23
  +----+------------+-----------+-----+-------------------+
22
24
  | id | first_name | last_name | age | custom_attributes |
@@ -24,11 +26,11 @@ describe 'ActiveRecordSource' do
24
26
  | 1 | chris | mo | 43 | |
25
27
  +----+------------+-----------+-----+-------------------+
26
28
  TABLE
27
- [p].to_table.text_table.to_s.should == expected
29
+ [p].to_table.to_s.should == expected
28
30
  end
29
31
 
30
32
  it 'handles custom serialization options in batch' do
31
- p = Person.create(:first_name => 'chrismo', :age => 43)
33
+ p = Person.create(first_name: 'chrismo', age: 43)
32
34
 
33
35
  expected = <<~TABLE
34
36
  +------------+-----+-----------+
@@ -40,14 +42,14 @@ describe 'ActiveRecordSource' do
40
42
  b = [p].to_table
41
43
 
42
44
  def b.serializable_options
43
- {:only => [:first_name, :age], :methods => [:year_born]}
45
+ { only: %i[first_name age], methods: [:year_born] }
44
46
  end
45
47
 
46
- b.text_table.to_s.should == expected
48
+ b.to_s.should == expected
47
49
  end
48
50
 
49
51
  it 'auto reloads records' do
50
- p = Person.create(:first_name => 'chrismo', :age => 43)
52
+ p = Person.create(first_name: 'chrismo', age: 43)
51
53
 
52
54
  expected = <<~TABLE
53
55
  +------------+-----+-----------+
@@ -59,9 +61,9 @@ describe 'ActiveRecordSource' do
59
61
  b = [p].to_table
60
62
 
61
63
  def b.serializable_options
62
- {:only => [:first_name, :age], :methods => [:year_born]}
64
+ { only: %i[first_name age], methods: [:year_born] }
63
65
  end
64
- b.text_table.to_s.should == expected
66
+ b.to_s.should == expected
65
67
 
66
68
  # update the value through another instance.
67
69
  Person.last.update_column(:age, 46)
@@ -73,11 +75,11 @@ describe 'ActiveRecordSource' do
73
75
  | chrismo | 46 | 1968 |
74
76
  +------------+-----+-----------+
75
77
  TABLE
76
- b.text_table.to_s.should == expected
78
+ b.to_s.should == expected
77
79
  end
78
80
 
79
81
  it 'handles column name partials' do
80
- p = Person.create(:first_name => 'chris', :last_name => 'mo', :age => 43)
82
+ p = Person.create(first_name: 'chris', last_name: 'mo', age: 43)
81
83
  expected = <<~TABLE
82
84
  +-------+------+-----+
83
85
  | first | last | age |
@@ -88,14 +90,14 @@ describe 'ActiveRecordSource' do
88
90
  b = [p].to_table
89
91
 
90
92
  def b.serializable_options
91
- {:only => [:first, :last, :age]}
93
+ { only: %i[first last age] }
92
94
  end
93
95
 
94
- b.text_table.to_s.should == expected
96
+ b.to_s.should == expected
95
97
  end
96
98
 
97
99
  it 'handles column name partials across words' do
98
- p = Person.create(:first_name => 'chris', :last_name => 'mo', :age => 43)
100
+ p = Person.create(first_name: 'chris', last_name: 'mo', age: 43)
99
101
  expected = <<~TABLE
100
102
  +--------+--------+-----+
101
103
  | f_name | l_name | age |
@@ -106,14 +108,14 @@ describe 'ActiveRecordSource' do
106
108
  b = [p].to_table
107
109
 
108
110
  def b.serializable_options
109
- {:only => [:f_name, :l_name, :age]}
111
+ { only: %i[f_name l_name age] }
110
112
  end
111
113
 
112
- b.text_table.to_s.should == expected
114
+ b.to_s.should == expected
113
115
  end
114
116
 
115
117
  it 'handles explicit column aliases' do
116
- p = Person.create(:first_name => 'chris', :last_name => 'mo', :age => 43)
118
+ p = Person.create(first_name: 'chris', last_name: 'mo', age: 43)
117
119
  expected = <<~TABLE
118
120
  +---------------+----------+-----+
119
121
  | primer_nombre | apellido | age |
@@ -129,10 +131,10 @@ describe 'ActiveRecordSource' do
129
131
  end
130
132
 
131
133
  def b.serializable_options
132
- {:only => [:first_name, :last_name, :age]}
134
+ { only: %i[first_name last_name age] }
133
135
  end
134
136
 
135
- b.text_table.to_s.should == expected
137
+ b.to_s.should == expected
136
138
  end
137
139
 
138
140
  it 'handles associations without aliases' do
@@ -141,7 +143,7 @@ describe 'ActiveRecordSource' do
141
143
  b = [s].to_table
142
144
 
143
145
  def b.serializable_options
144
- {:only => [:name], :include => {:account => {:only => [:name, :tax_identification_number]}}}
146
+ { only: [:name], include: { account: { only: %i[name tax_identification_number] } } }
145
147
  end
146
148
 
147
149
  expected = <<~TABLE
@@ -154,7 +156,7 @@ describe 'ActiveRecordSource' do
154
156
  +----------+---------+---------------------------+
155
157
  TABLE
156
158
 
157
- b.text_table.to_s.should == expected
159
+ b.to_s.should == expected
158
160
  end
159
161
 
160
162
  it 'handles associations with aliases' do
@@ -163,7 +165,7 @@ describe 'ActiveRecordSource' do
163
165
  b = [s].to_table
164
166
 
165
167
  def b.serializable_options
166
- {:only => [:name], :include => {:account => {:only => [:name, :tax_id]}}}
168
+ { only: [:name], include: { account: { only: %i[name tax_id] } } }
167
169
  end
168
170
 
169
171
  expected = <<~TABLE
@@ -176,7 +178,7 @@ describe 'ActiveRecordSource' do
176
178
  +----------+---------+--------+
177
179
  TABLE
178
180
 
179
- b.text_table.to_s.should == expected
181
+ b.to_s.should == expected
180
182
  end
181
183
 
182
184
  it 'retains serializable_options ordering'
@@ -187,7 +189,7 @@ describe 'ActiveRecordSource' do
187
189
 
188
190
  # may need/want to handle the hash resulting from an association differently from the hash resulting from a method/attr
189
191
  it 'supports field with hash contents' do
190
- p = Person.create(first_name: 'chrismo', custom_attributes: {skills: {instrument: 'piano', style: 'jazz'}})
192
+ p = Person.create(first_name: 'chrismo', custom_attributes: { skills: { instrument: 'piano', style: 'jazz' } })
191
193
  b = [p].to_table
192
194
 
193
195
  a = format_ids([p.id])[0]
@@ -201,12 +203,12 @@ describe 'ActiveRecordSource' do
201
203
  +----+------------+-----------+-----+----------------------------------------+
202
204
  TABLE
203
205
 
204
- b.text_table.to_s.should == expected
206
+ b.to_s.should == expected
205
207
  end
206
208
 
207
209
  it 'supports multiple rows with different column counts' do
208
- p2 = Person.create(first_name: 'romer', custom_attributes: {instrument: 'kazoo'})
209
- p1 = Person.create(first_name: 'chrismo', custom_attributes: {instrument: 'piano', style: 'jazz'})
210
+ p2 = Person.create(first_name: 'romer', custom_attributes: { instrument: 'kazoo' })
211
+ p1 = Person.create(first_name: 'chrismo', custom_attributes: { instrument: 'piano', style: 'jazz' })
210
212
  p3 = Person.create(first_name: 'glv', custom_attributes: {})
211
213
  batch = [p2, p1, p3].to_table
212
214
 
@@ -224,12 +226,12 @@ describe 'ActiveRecordSource' do
224
226
  +----+------------+-----------+-----+------------+-----------+
225
227
  TABLE
226
228
 
227
- batch.text_table.to_s.should == expected
229
+ batch.to_s.should == expected
228
230
  end
229
231
 
230
232
  it 'supports consistent ordering of dynamic columns' do
231
- p1 = Person.create(first_name: 'chrismo', custom_attributes: {instrument: 'piano', style: 'jazz'})
232
- p2 = Person.create(first_name: 'romer', custom_attributes: {hobby: 'games'})
233
+ p1 = Person.create(first_name: 'chrismo', custom_attributes: { instrument: 'piano', style: 'jazz' })
234
+ p2 = Person.create(first_name: 'romer', custom_attributes: { hobby: 'games' })
233
235
  batch = [p1, p2].to_table
234
236
 
235
237
  a, b = format_ids([p1.id, p2.id])
@@ -245,7 +247,7 @@ describe 'ActiveRecordSource' do
245
247
  +----+------------+-----------+-----+--------+------------+--------+
246
248
  TABLE
247
249
 
248
- batch.text_table.to_s.should == expected
250
+ batch.to_s.should == expected
249
251
  end
250
252
 
251
253
  it 'handles AR instance without an association present' do
@@ -253,7 +255,7 @@ describe 'ActiveRecordSource' do
253
255
  b = [s].to_table
254
256
 
255
257
  def b.serializable_options
256
- {:only => [:name], :include => {:account => {:only => [:name, :tax_id]}}}
258
+ { only: [:name], include: { account: { only: %i[name tax_id] } } }
257
259
  end
258
260
 
259
261
  expected = <<~TABLE
@@ -264,11 +266,11 @@ describe 'ActiveRecordSource' do
264
266
  +----------+
265
267
  TABLE
266
268
 
267
- b.text_table.to_s.should == expected
269
+ b.to_s.should == expected
268
270
  end
269
271
 
270
272
  it 'properly groups when original columns not sequential' do
271
- s2 = Supplier.create(name: 'sup. two', custom_attributes: {a: 1})
273
+ s2 = Supplier.create(name: 'sup. two', custom_attributes: { a: 1 })
272
274
 
273
275
  def s2.foo
274
276
  ''
@@ -278,7 +280,7 @@ describe 'ActiveRecordSource' do
278
280
 
279
281
  # methods need Columns as well
280
282
  def b.serializable_options
281
- {:only => [:name, :custom_attributes], :methods => [:foo]}
283
+ { only: %i[name custom_attributes], methods: [:foo] }
282
284
  end
283
285
 
284
286
  expected = <<~TABLE
@@ -291,12 +293,12 @@ describe 'ActiveRecordSource' do
291
293
  +----------+------+-------------------+
292
294
  TABLE
293
295
 
294
- b.text_table.to_s.should == expected
296
+ b.to_s.should == expected
295
297
  end
296
298
 
297
299
  it 'supports one to many association' do
298
300
  p = Parent.create(name: 'parent')
299
- c = Child.create(name: 'child', parent: p)
301
+ Child.create(name: 'child', parent: p)
300
302
 
301
303
  b = [p].to_table
302
304
 
@@ -310,32 +312,32 @@ describe 'ActiveRecordSource' do
310
312
  TABLE
311
313
 
312
314
  def b.serializable_options
313
- {:include => {:children => {:only => [:name]}}}
315
+ { include: { children: { only: [:name] } } }
314
316
  end
315
317
 
316
- b.text_table.to_s.should == expected
318
+ b.to_s.should == expected
317
319
  end
318
320
 
319
321
  def format_ids(ary)
320
- ary.map {|value| " #{value.to_s.ljust(3)}" }
322
+ ary.map { |value| " #{value.to_s.ljust(3)}" }
321
323
  end
322
324
 
323
325
  describe 'fold un-sourced attributes into source hash' do
324
326
  let(:obj) { Object.new.extend Tablesmith::ActiveRecordSource }
325
327
 
326
328
  it 'should handle simple hash' do
327
- obj.fold_un_sourced_attributes_into_source_hash(:foo, {a: 1, b: 2}).should == {foo: {a: 1, b: 2}}
329
+ obj.fold_un_sourced_attributes_into_source_hash(:foo, a: 1, b: 2).should == { foo: { a: 1, b: 2 } }
328
330
  end
329
331
 
330
332
  it 'should handle nested hashes' do
331
- before = {'name' => 'chris', account: {'name' => 'account_name'}}
332
- expected = {foo: {'name' => 'chris'}, account: {'name' => 'account_name'}}
333
+ before = { 'name' => 'chris', account: { 'name' => 'account_name' } }
334
+ expected = { foo: { 'name' => 'chris' }, account: { 'name' => 'account_name' } }
333
335
  obj.fold_un_sourced_attributes_into_source_hash(:foo, before).should == expected
334
336
  end
335
337
 
336
338
  it 'should handle deep nested hashes' do
337
- before = {'name' => 'chris', account: {'id' => {'name' => 'account_name', 'number' => 123456}}}
338
- expected = {foo: {'name' => 'chris'}, account: {'id' => {'name' => 'account_name', 'number' => 123456}}}
339
+ before = { 'name' => 'chris', account: { 'id' => { 'name' => 'account_name', 'number' => 123_456 } } }
340
+ expected = { foo: { 'name' => 'chris' }, account: { 'id' => { 'name' => 'account_name', 'number' => 123_456 } } }
339
341
  obj.fold_un_sourced_attributes_into_source_hash(:foo, before).should == expected
340
342
  end
341
343
  end
@@ -344,15 +346,15 @@ describe 'ActiveRecordSource' do
344
346
  let(:obj) { Object.new.extend Tablesmith::ActiveRecordSource }
345
347
 
346
348
  it 'should flatten inner hash' do
347
- before = {foo: {'name' => 'chris'}, account: {'name' => 'account_name'}}
348
- expected = {'foo.name' => 'chris', 'account.name' => 'account_name'}
349
+ before = { foo: { 'name' => 'chris' }, account: { 'name' => 'account_name' } }
350
+ expected = { 'foo.name' => 'chris', 'account.name' => 'account_name' }
349
351
 
350
352
  obj.flatten_inner_hashes(before).should == expected
351
353
  end
352
354
 
353
355
  it 'should to_s deep nested hashes' do
354
- before = {foo: {'name' => 'chris'}, account: {'id' => {'name' => 'account_name', 'number' => 123456}}}
355
- expected = {'foo.name' => 'chris', "account.id" => "{\"name\"=>\"account_name\", \"number\"=>123456}"}
356
+ before = { foo: { 'name' => 'chris' }, account: { 'id' => { 'name' => 'account_name', 'number' => 123_456 } } }
357
+ expected = { 'foo.name' => 'chris', 'account.id' => '{"name"=>"account_name", "number"=>123456}' }
356
358
 
357
359
  obj.flatten_inner_hashes(before).should == expected
358
360
  end