table_print 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -46,6 +46,24 @@ on your model.
46
46
  If you're not using ActiveRecord, the TablePrint default is to show all the methods on your object. Thus, the <b>:include</b>
47
47
  option is useless at the moment. You are still able to use <b>:only</b> and <b>:include</b>.
48
48
 
49
+ === Column options
50
+
51
+ Pass options to individual columns through the options hash by using the display method as the hash key. Eg, if you wanted
52
+ to rename the <b>pay_rate</b> column to <b>Special User Payment Teir</b>,
53
+
54
+ tp User.all, :pay_rate => {:name => "Special User Payment Teir"}
55
+
56
+ Columns have other options, including:
57
+
58
+ <b>name</b>: Use this in the column header instead of the name of the display method.
59
+
60
+ <b>max_field_length:</b> <i>(default: 30)</i> Field lengths are determined based on the data in the column. Setting your own max
61
+ will ensure that the column is as skinny as possible, but never above the number you provide.
62
+
63
+ <b>field_length:</b> Useful for very large data sets, this option will explicitly set the column width regardless of the data
64
+ it contains.
65
+
66
+
49
67
  == Contributing to table_print
50
68
 
51
69
  * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.1
1
+ 0.1.2
data/lib/table_print.rb CHANGED
@@ -13,6 +13,16 @@
13
13
 
14
14
  class TablePrint
15
15
 
16
+ OBJECT_CLASSES = [String, Bignum, Regexp, ThreadError, Numeric, SystemStackError, IndexError,
17
+ SecurityError, SizedQueue, IO, Range, Object, Exception, NoMethodError, TypeError, Integer, Dir,
18
+ ZeroDivisionError, Kernel, RegexpError, SystemExit, NotImplementedError, Hash,
19
+ Interrupt, SyntaxError, Enumerable, Struct, Class, Continuation, IOError, Proc,
20
+ RangeError, Data, Thread, Array, NoMemoryError, Time, MatchData,
21
+ ConditionVariable, Method, Mutex, StopIteration, Comparable, ArgumentError, Float,
22
+ FloatDomainError, UnboundMethod, ThreadGroup, Precision, RuntimeError, FalseClass, Fixnum, Queue,
23
+ StandardError, EOFError, LoadError, NameError, NilClass, TrueClass, MatchingData,
24
+ LocalJumpError, Binding, SignalException, SystemCallError, File, ScriptError, Module, Symbol]
25
+
16
26
  # TODO: make options for things like MAX_FIELD_LENGTH
17
27
  # TODO: make options for things like separator
18
28
  # TODO: make options for things like column order
@@ -24,9 +34,6 @@ class TablePrint
24
34
  def tp(data, options = {})
25
35
  data = wrap(data).compact
26
36
 
27
- # TODO: need to do a better job of handling options.
28
- options[:column_options] ||= {}
29
-
30
37
  # nothing to see here
31
38
  if data.empty?
32
39
  return "No data."
@@ -45,7 +52,7 @@ class TablePrint
45
52
  # TODO: don't check field length on fixed-width columns
46
53
 
47
54
  # make columns for all the display methods
48
- columns = display_methods.collect { |m| Column.new(data, m, options[:column_options][m]) }
55
+ columns = display_methods.collect { |m| Column.new(data, m, options[m] || options[m.to_sym]) }
49
56
 
50
57
  output = [] # a list of rows. we'll join this with newlines when we're done
51
58
 
@@ -131,7 +138,13 @@ class TablePrint
131
138
  return [] if [Float, Fixnum, String, Numeric, Array, Hash].include? data_obj.class
132
139
 
133
140
  # custom class
134
- methods = data_obj.class.instance_methods - Object.instance_methods
141
+ methods = data_obj.class.instance_methods
142
+ OBJECT_CLASSES.each do |oclass|
143
+ if data_obj.is_a? oclass
144
+ methods = methods - oclass.instance_methods # we're only interested in custom methods, not ruby core methods
145
+ end
146
+ end
147
+
135
148
  methods.delete_if { |m| m[-1].chr == "=" } # don't use assignment methods
136
149
  methods.map! { |m| m.to_s } # make any symbols into strings
137
150
  methods
@@ -164,12 +177,14 @@ class TablePrint
164
177
  attr_accessor :name, :display_method, :options, :data, :field_length, :max_field_length
165
178
 
166
179
  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
180
+ self.options = options || {} # could have been passed an explicit nil
169
181
  self.display_method = display_method
170
- self.name = options[:name] || display_method.gsub("_", " ")
171
- self.max_field_length = options[:max_field_length] || 30
182
+ self.name = self.options[:name] || display_method.gsub("_", " ")
183
+ self.max_field_length = self.options[:max_field_length] || 30
172
184
  self.max_field_length = [self.max_field_length, 1].max # numbers less than one are meaningless
185
+
186
+ # initialization
187
+ self.initialize_field_length(data)
173
188
  end
174
189
 
175
190
  def formatted_header
@@ -180,24 +195,34 @@ class TablePrint
180
195
  "%-#{self.field_length}s" % truncate(data_obj.send(self.display_method).to_s)
181
196
  end
182
197
 
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
198
+ def initialize_field_length(data)
199
+ # skip all this nonsense if we've been explicitly told what to do
200
+ if self.options[:field_length] and self.options[:field_length] > 0
201
+ length = self.options[:field_length]
202
+ else
203
+ length = self.name.length # it has to at least be long enough for the column header!
204
+
205
+ start = Time.now
206
+ data.each do |data_obj|
207
+ next if data_obj.nil?
208
+
209
+ # fixed-width fields don't require the full loop
210
+ case data_obj.send(self.display_method)
211
+ when Time
212
+ length = data_obj.send(self.display_method).to_s.length
213
+ break
214
+ when TrueClass, FalseClass
215
+ length = [5, length].max
216
+ break
217
+ end
218
+
219
+ length = [length, data_obj.send(self.display_method).to_s.length].max
220
+ break if length >= self.max_field_length # we're never going to longer than the global max, so why keep going
221
+ break if (Time.now - start) > 2 # assume if we loop for more than 2s that we've made it through a representative sample, and bail
222
+ end
192
223
  end
193
224
 
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
225
+ self.field_length = [length, self.max_field_length].min # never bigger than the max
201
226
  end
202
227
 
203
228
  private
@@ -223,3 +248,5 @@ module Kernel
223
248
 
224
249
  module_function :tp
225
250
  end
251
+
252
+
data/table_print.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{table_print}
8
- s.version = "0.1.1"
8
+ s.version = "0.1.2"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Chris Doyle"]
12
- s.date = %q{2011-04-13}
12
+ s.date = %q{2011-04-17}
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
14
  s.email = %q{archslide@gmail.com}
15
15
  s.extra_rdoc_files = [
data/test/test_column.rb CHANGED
@@ -17,23 +17,57 @@ class TestTablePrint < Test::Unit::TestCase
17
17
  context 'Instantiating a Column' do
18
18
  context 'with a display_method' do
19
19
  setup do
20
- @column = TablePrint::Column.new([], "display_method_name")
20
+ @column = TablePrint::Column.new([], "to_s")
21
21
  end
22
22
  should 'remember the display method' do
23
- assert_equal "display_method_name", @column.display_method
23
+ assert_equal "to_s", @column.display_method
24
24
  end
25
25
  should 'set the name' do
26
- assert_equal "display method name", @column.name
26
+ assert_equal "to s", @column.name
27
27
  end
28
28
  end
29
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"})
30
+ context 'with options including' do
31
+ context 'name' do
32
+ setup do
33
+ @column = TablePrint::Column.new([], "first", {:name => "test_tube"})
34
+ end
35
+
36
+ should 'set the name according to the options' do
37
+ assert_equal "test_tube", @column.name
38
+ end
33
39
  end
34
40
 
35
- should 'set the name according to the options' do
36
- assert_equal "test_tube", @column.name
41
+ context 'a field_length' do
42
+ context 'that is valid' do
43
+ setup do
44
+ @column = TablePrint::Column.new(["short"], "first", {:field_length => 20})
45
+ end
46
+
47
+ should 'set the field length according to the options' do
48
+ assert_equal 20, @column.field_length
49
+ end
50
+ end
51
+
52
+ context 'that is less than 1' do
53
+ setup do
54
+ @column = TablePrint::Column.new(["short"], "first", {:field_length => 0})
55
+ end
56
+
57
+ should 'ignore the field_length' do
58
+ assert_equal 5, @column.field_length
59
+ end
60
+ end
61
+
62
+ context 'that is bigger than the max' do
63
+ setup do
64
+ @column = TablePrint::Column.new(["short"], "first", {:field_length => 20, :max_field_length => 10})
65
+ end
66
+
67
+ should 'respect the max_field_length' do
68
+ assert_equal 10, @column.field_length
69
+ end
70
+ end
37
71
  end
38
72
  end
39
73
  end
@@ -89,6 +123,13 @@ class TestTablePrint < Test::Unit::TestCase
89
123
  assert_equal 5, TablePrint::Column.new([[false]], "first", :name => "dur").field_length
90
124
  end
91
125
  end
126
+
127
+ context 'when the column is boolean and the data is not the limiting factor' do
128
+ should 'be the column name length' do
129
+ assert_equal 7, TablePrint::Column.new([[true]], "unshift").field_length
130
+ assert_equal 8, TablePrint::Column.new([[false]], "unshift", :name => "durables").field_length
131
+ end
132
+ end
92
133
  end
93
134
 
94
135
  end
@@ -60,6 +60,18 @@ class ManyMethods
60
60
  end
61
61
  end
62
62
 
63
+ class StringInheritor < String
64
+ attr_accessor :title
65
+ end
66
+
67
+ class ArrayInheritor < Array
68
+ attr_accessor :title
69
+ end
70
+
71
+ class HashInheritor < Hash
72
+ attr_accessor :title
73
+ end
74
+
63
75
  class TestTablePrint < Test::Unit::TestCase
64
76
 
65
77
  # TODO: active record tests if defined?(ActiveRecord)
@@ -104,6 +116,22 @@ class TestTablePrint < Test::Unit::TestCase
104
116
  should 'only be the attr setter' do
105
117
  assert_equal ["title"], @tp._get_default_display_methods(OneAttrAccessor.new)
106
118
  end
119
+
120
+ context 'that subclasses Hash' do
121
+ should 'only be the attr setter' do
122
+ assert_equal ["title"], @tp._get_default_display_methods(HashInheritor.new)
123
+ end
124
+ end
125
+ context 'that subclasses Array' do
126
+ should 'only be the attr setter' do
127
+ assert_equal ["title"], @tp._get_default_display_methods(ArrayInheritor.new)
128
+ end
129
+ end
130
+ context 'that subclasses String' do
131
+ should 'only be the attr setter' do
132
+ assert_equal ["title"], @tp._get_default_display_methods(StringInheritor.new)
133
+ end
134
+ end
107
135
  end
108
136
 
109
137
  context 'The display methods for a custom class using the :only option' do
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: 25
4
+ hash: 31
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 1
9
- - 1
10
- version: 0.1.1
9
+ - 2
10
+ version: 0.1.2
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-13 00:00:00 -07:00
18
+ date: 2011-04-17 00:00:00 -07:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency