table_print 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.0
1
+ 0.1.1
data/lib/table_print.rb CHANGED
@@ -3,6 +3,9 @@
3
3
  # handle multi-level includes like 'tp User.all, :include => "blogs.title"' and ActiveRecord associations
4
4
  # allow other output venues besides 'puts'
5
5
  # allow fine-grained formatting
6
+ # on-the-fly column definitions (pass a proc as an include, eg 'tp User.all, :include => {:column_name => "Zodiac", :display_method => lambda {|u| find_zodiac_sign(u.birthday)}}')
7
+ # allow user to pass ActiveRelation instead of a data array? That could open up so many options!
8
+ # a :short_booleans method could save a little space (replace true/false with T/F or 1/0)
6
9
  #
7
10
  # bugs
8
11
  #
@@ -10,8 +13,6 @@
10
13
 
11
14
  class TablePrint
12
15
 
13
- MAX_FIELD_LENGTH = 30
14
-
15
16
  # TODO: make options for things like MAX_FIELD_LENGTH
16
17
  # TODO: make options for things like separator
17
18
  # TODO: make options for things like column order
@@ -23,6 +24,9 @@ class TablePrint
23
24
  def tp(data, options = {})
24
25
  data = wrap(data).compact
25
26
 
27
+ # TODO: need to do a better job of handling options.
28
+ options[:column_options] ||= {}
29
+
26
30
  # nothing to see here
27
31
  if data.empty?
28
32
  return "No data."
@@ -40,37 +44,26 @@ class TablePrint
40
44
  # TODO: stop checking field length once we hit the max
41
45
  # TODO: don't check field length on fixed-width columns
42
46
 
43
- field_lengths = {}
44
-
45
- # column headers
46
- display_methods.each do |m|
47
- field_lengths[m] = m.to_s.length
48
- end
49
-
50
- data.each do |obj|
51
- display_methods.each do |m|
52
- field_value = truncate(obj.send(m).to_s)
53
- field_lengths[m] = [field_lengths[m], field_value.length].max
54
- end
55
- end
47
+ # make columns for all the display methods
48
+ columns = display_methods.collect { |m| Column.new(data, m, options[:column_options][m]) }
56
49
 
57
- output = []
50
+ output = [] # a list of rows. we'll join this with newlines when we're done
58
51
 
52
+ # column headers
59
53
  row = []
60
- display_methods.each do |m|
61
- field_value = truncate(m.to_s)
62
- field_length = field_lengths[m]
63
- row << ("%-#{field_length}s" % field_value.upcase)
54
+ columns.each do |column|
55
+ row << column.formatted_header
64
56
  end
65
57
  output << row.join(separator)
58
+
59
+ # a row of hyphens to separate the headers from the data
66
60
  output << ("-" * row.join(separator).length)
67
61
 
68
- data.each do |obj|
62
+ # the data!
63
+ data.each do |data_obj|
69
64
  row = []
70
- display_methods.each do |m|
71
- field_value = truncate(obj.send(m).to_s)
72
- field_length = field_lengths[m]
73
- row << ("%-#{field_length}s" % field_value)
65
+ columns.each do |column|
66
+ row << column.formatted_field_value(data_obj)
74
67
  end
75
68
  output << row.join(separator)
76
69
  end
@@ -80,14 +73,6 @@ class TablePrint
80
73
 
81
74
  private
82
75
 
83
- def truncate(field_value)
84
- if field_value.length > MAX_FIELD_LENGTH
85
- field_value = field_value[0..MAX_FIELD_LENGTH-1]
86
- field_value[-3..-1] = "..."
87
- end
88
- field_value
89
- end
90
-
91
76
  def get_display_methods(data_obj, options)
92
77
  # determine what methods we're going to use
93
78
 
@@ -153,6 +138,7 @@ class TablePrint
153
138
  end
154
139
 
155
140
  def clean_display_methods(data_obj, display_methods)
141
+ # TODO: this should probably be inside Column
156
142
  clean_methods = []
157
143
  display_methods.each do |m|
158
144
  next if m.nil?
@@ -173,6 +159,58 @@ class TablePrint
173
159
  [object]
174
160
  end
175
161
  end
162
+
163
+ class Column
164
+ attr_accessor :name, :display_method, :options, :data, :field_length, :max_field_length
165
+
166
+ def initialize(data, display_method, options = {})
167
+ options ||= {} # could have been passed an explicit nil
168
+ self.data = data # HACK? would rather not keep pointers to the data set all over the place
169
+ self.display_method = display_method
170
+ self.name = options[:name] || display_method.gsub("_", " ")
171
+ self.max_field_length = options[:max_field_length] || 30
172
+ self.max_field_length = [self.max_field_length, 1].max # numbers less than one are meaningless
173
+ end
174
+
175
+ def formatted_header
176
+ "%-#{self.field_length}s" % truncate(self.name.upcase)
177
+ end
178
+
179
+ def formatted_field_value(data_obj)
180
+ "%-#{self.field_length}s" % truncate(data_obj.send(self.display_method).to_s)
181
+ end
182
+
183
+ def field_length
184
+ return @field_length if defined?(@field_length) # we don't want to loop every time this is called!
185
+
186
+ # fixed-width fields don't require the full loop below
187
+ case data.first.send(self.display_method)
188
+ when Time
189
+ return [data.first.send(self.display_method).to_s.length, self.max_field_length].min
190
+ when TrueClass, FalseClass
191
+ return 5
192
+ end
193
+
194
+ length = self.name.length
195
+ self.data.each do |data_obj|
196
+ length = [length, data_obj.send(self.display_method).to_s.length].max
197
+ break if length >= self.max_field_length # we're never going to longer than the global max, so why keep going
198
+ end
199
+ @field_length = [length, self.max_field_length].min
200
+ @field_length
201
+ end
202
+
203
+ private
204
+
205
+ def truncate(field_value)
206
+ copy = String.new(field_value)
207
+ if copy.length > self.max_field_length
208
+ copy = copy[0..self.max_field_length-1]
209
+ copy[-3..-1] = "..." unless self.max_field_length <= 3 # don't use ellipses when the string is tiny
210
+ end
211
+ copy
212
+ end
213
+ end
176
214
  end
177
215
 
178
216
  module Kernel
@@ -180,7 +218,7 @@ module Kernel
180
218
  start = Time.now
181
219
  table_print = TablePrint.new
182
220
  puts table_print.tp(data, options)
183
- return Time.now - start
221
+ Time.now - start
184
222
  end
185
223
 
186
224
  module_function :tp
@@ -0,0 +1,65 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{table_print}
8
+ s.version = "0.1.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Chris Doyle"]
12
+ s.date = %q{2011-04-13}
13
+ s.description = %q{TablePrint formats an object or array of objects into columns for easy reading. To do this, it assumes the objects in your array all respond to the same methods (vs pretty_print or awesome_print, who can't create columns because your objects could be entirely different).}
14
+ s.email = %q{archslide@gmail.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE.txt",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ "Gemfile",
22
+ "Gemfile.lock",
23
+ "LICENSE.txt",
24
+ "README.rdoc",
25
+ "Rakefile",
26
+ "VERSION",
27
+ "lib/table_print.rb",
28
+ "table_print.gemspec",
29
+ "test/helper.rb",
30
+ "test/test_column.rb",
31
+ "test/test_table_print.rb"
32
+ ]
33
+ s.homepage = %q{http://github.com/arches/table_print}
34
+ s.licenses = ["MIT"]
35
+ s.require_paths = ["lib"]
36
+ s.rubygems_version = %q{1.5.2}
37
+ s.summary = %q{Turn objects into nicely formatted columns for easy reading}
38
+ s.test_files = [
39
+ "test/helper.rb",
40
+ "test/test_column.rb",
41
+ "test/test_table_print.rb"
42
+ ]
43
+
44
+ if s.respond_to? :specification_version then
45
+ s.specification_version = 3
46
+
47
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
48
+ s.add_development_dependency(%q<shoulda>, [">= 0"])
49
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
50
+ s.add_development_dependency(%q<jeweler>, ["~> 1.5.2"])
51
+ s.add_development_dependency(%q<rcov>, [">= 0"])
52
+ else
53
+ s.add_dependency(%q<shoulda>, [">= 0"])
54
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
55
+ s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
56
+ s.add_dependency(%q<rcov>, [">= 0"])
57
+ end
58
+ else
59
+ s.add_dependency(%q<shoulda>, [">= 0"])
60
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
61
+ s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
62
+ s.add_dependency(%q<rcov>, [">= 0"])
63
+ end
64
+ end
65
+
@@ -0,0 +1,94 @@
1
+ require 'helper'
2
+
3
+ class TablePrint
4
+ class Column
5
+ def _truncate(field_value)
6
+ truncate(field_value)
7
+ end
8
+ end
9
+ end
10
+
11
+ class TestTablePrint < Test::Unit::TestCase
12
+
13
+ # TODO: active record tests if defined?(ActiveRecord)
14
+
15
+ # Vaguely ordered from most to least granular
16
+
17
+ context 'Instantiating a Column' do
18
+ context 'with a display_method' do
19
+ setup do
20
+ @column = TablePrint::Column.new([], "display_method_name")
21
+ end
22
+ should 'remember the display method' do
23
+ assert_equal "display_method_name", @column.display_method
24
+ end
25
+ should 'set the name' do
26
+ assert_equal "display method name", @column.name
27
+ end
28
+ end
29
+
30
+ context 'with a column name in the options' do
31
+ setup do
32
+ @column = TablePrint::Column.new([], "display_method_name", {:name => "test_tube"})
33
+ end
34
+
35
+ should 'set the name according to the options' do
36
+ assert_equal "test_tube", @column.name
37
+ end
38
+ end
39
+ end
40
+
41
+ context 'The truncate function' do
42
+ should 'let short strings pass through' do
43
+ assert_equal "asdf", TablePrint::Column.new([], "")._truncate("asdf")
44
+ end
45
+
46
+ should 'truncate long strings with ellipses' do
47
+ assert_equal "123456789012345678901234567...", TablePrint::Column.new([], "")._truncate("1234567890123456789012345678901234567890")
48
+ end
49
+
50
+ context 'when given a max length in the options' do
51
+ should 'truncate long strings with ellipses' do
52
+ assert_equal "1234567...", TablePrint::Column.new([], "", :max_field_length => 10)._truncate("1234567890123456789012345678901234567890")
53
+ end
54
+ end
55
+
56
+ context 'when the max length is tiny' do
57
+ should 'truncate long strings without ellipses' do
58
+ assert_equal "1", TablePrint::Column.new([], "", :max_field_length => -10)._truncate("1234567890123456789012345678901234567890")
59
+ assert_equal "1", TablePrint::Column.new([], "", :max_field_length => 0)._truncate("1234567890123456789012345678901234567890")
60
+ assert_equal "1", TablePrint::Column.new([], "", :max_field_length => 1)._truncate("1234567890123456789012345678901234567890")
61
+ assert_equal "12", TablePrint::Column.new([], "", :max_field_length => 2)._truncate("1234567890123456789012345678901234567890")
62
+ assert_equal "123", TablePrint::Column.new([], "", :max_field_length => 3)._truncate("1234567890123456789012345678901234567890")
63
+ assert_equal "1...", TablePrint::Column.new([], "", :max_field_length => 4)._truncate("1234567890123456789012345678901234567890")
64
+ end
65
+ end
66
+ end
67
+
68
+ context 'The field length function' do
69
+ should 'find the maximum width of the data' do
70
+ assert_equal 11, TablePrint::Column.new(["hello there"], "to_s").field_length
71
+ end
72
+
73
+ context 'when the data is longer than the max_field_length' do
74
+ should 'equal the max field length' do
75
+ assert_equal 5, TablePrint::Column.new(["hello there"], "to_s", :max_field_length => 5).field_length
76
+ end
77
+ end
78
+
79
+ context 'when the column name is longer than the data' do
80
+ should 'reflect the column name length' do
81
+ assert_equal 4, TablePrint::Column.new(["he"], "to_s", :max_field_length => 5).field_length
82
+ assert_equal 12, TablePrint::Column.new(["hello"], "to_s", :name => "foobar THIS!").field_length
83
+ end
84
+ end
85
+
86
+ context 'when the column is boolean and the data is the limiting factor' do
87
+ should 'always be 5' do
88
+ assert_equal 5, TablePrint::Column.new([[true]], "first", :name => "dur").field_length
89
+ assert_equal 5, TablePrint::Column.new([[false]], "first", :name => "dur").field_length
90
+ end
91
+ end
92
+ end
93
+
94
+ end
@@ -29,10 +29,6 @@ class MyClass
29
29
  end
30
30
 
31
31
  class TablePrint
32
- def _truncate(field_value)
33
- truncate(field_value)
34
- end
35
-
36
32
  def _get_display_methods(data_obj, options)
37
33
  get_display_methods(data_obj, options)
38
34
  end
@@ -85,16 +81,6 @@ class TestTablePrint < Test::Unit::TestCase
85
81
  end
86
82
  end
87
83
 
88
- context 'The truncate function' do
89
- should 'let short strings pass through' do
90
- assert_equal "asdf", @tp._truncate("asdf")
91
- end
92
-
93
- should 'truncate long strings with ellipses' do
94
- assert_equal "123456789012345678901234567...", @tp._truncate("1234567890123456789012345678901234567890")
95
- end
96
- end
97
-
98
84
  context 'The clean_display_methods function' do
99
85
  should 'only give back valid methods' do
100
86
  assert_equal [], @tp._clean_display_methods(ManyMethods.new, [""])
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: table_print
3
3
  version: !ruby/object:Gem::Version
4
- hash: 27
4
+ hash: 25
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 1
9
- - 0
10
- version: 0.1.0
9
+ - 1
10
+ version: 0.1.1
11
11
  platform: ruby
12
12
  authors:
13
13
  - Chris Doyle
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-04-11 00:00:00 -07:00
18
+ date: 2011-04-13 00:00:00 -07:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -96,7 +96,9 @@ files:
96
96
  - Rakefile
97
97
  - VERSION
98
98
  - lib/table_print.rb
99
+ - table_print.gemspec
99
100
  - test/helper.rb
101
+ - test/test_column.rb
100
102
  - test/test_table_print.rb
101
103
  has_rdoc: true
102
104
  homepage: http://github.com/arches/table_print
@@ -134,4 +136,5 @@ specification_version: 3
134
136
  summary: Turn objects into nicely formatted columns for easy reading
135
137
  test_files:
136
138
  - test/helper.rb
139
+ - test/test_column.rb
137
140
  - test/test_table_print.rb