table_display 0.5.0

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.
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ .DS_Store
2
+ table_display-*.gem
3
+ Gemfile.lock
4
+ table_display_test.db
5
+ test/log/test.log
data/Gemfile ADDED
@@ -0,0 +1,15 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Declare your gem's dependencies in transaction_isolation_level.gemspec.
4
+ # Bundler will treat runtime dependencies like base dependencies, and
5
+ # development dependencies will be added by default to the :development group.
6
+ gemspec
7
+
8
+ # Declare any dependencies that are still in development here instead of in
9
+ # your gemspec. These might include edge Rails or gems from your path or
10
+ # Git. Remember to move these dependencies to your gemspec before releasing
11
+ # your gem to rubygems.org.
12
+
13
+ # To use debugger
14
+ # gem 'ruby-debug19', :require => 'ruby-debug'
15
+ # gem 'ruby-debug'
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Will Bryant, Sekuda Ltd
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,82 @@
1
+ Table Display
2
+ =============
3
+
4
+ Adds support for displaying your ActiveRecord tables, named scopes, collections, or
5
+ plain arrays in a table view when working in rails console, shell, or email template.
6
+
7
+ Enumerable#to_table returns the printable strings; Object#pt calls #to_table on its
8
+ first argument and puts out the result.
9
+
10
+ Columns you haven't loaded (eg. from using :select) are omitted, and derived/calculated
11
+ columns (eg. again, from using :select) are added.
12
+
13
+ Both #to_table and Object#pt methods take :only, :except, and :methods which work like
14
+ the #to_xml method to change what attributes/methods are output.
15
+
16
+ The normal output uses #inspect on the data values to make them printable, so you can
17
+ see what type the values had. When that's inconvenient or you'd prefer direct display,
18
+ you can pass the option :inspect => false to disable inspection.
19
+
20
+
21
+ Example
22
+ =======
23
+
24
+ # You can call the to_table method:
25
+ >> puts Project.find(31).tasks.to_table
26
+ +----+------------+------------------------+------------------+--------------------------------+--------------------------------+--------------------------------+
27
+ | id | project_id | description | due_on | completed_at | created_at | updated_at |
28
+ +----+------------+------------------------+------------------+--------------------------------+--------------------------------+--------------------------------+
29
+ | 1 | 31 | "Write a handy plugin" | Wed, 25 Mar 2009 | Tue Mar 24 23:17:05 +1300 2009 | Mon Mar 23 09:11:02 +1300 2009 | Tue Mar 24 23:17:05 +1300 2009 |
30
+ | 2 | 31 | "Blog the plugin" | Sun, 05 Apr 2009 | nil | Mon Mar 23 09:11:46 +1300 2009 | Mon Mar 23 09:11:46 +1300 2009 |
31
+ +----+------------+------------------------+------------------+--------------------------------+--------------------------------+--------------------------------+
32
+
33
+ # Or equivalently, use "pt" (like pp, but in a table):
34
+ >> pt Customer.find(31).purchases
35
+ +----+------------+------------------------+------------------+--------------------------------+--------------------------------+--------------------------------+
36
+ | id | project_id | description | due_on | completed_at | created_at | updated_at |
37
+ +----+------------+------------------------+------------------+--------------------------------+--------------------------------+--------------------------------+
38
+ | 1 | 31 | "Write a handy plugin" | Wed, 25 Mar 2009 | Tue Mar 24 23:17:05 +1300 2009 | Mon Mar 23 09:11:02 +1300 2009 | Tue Mar 24 23:17:05 +1300 2009 |
39
+ | 2 | 31 | "Blog the plugin" | Sun, 05 Apr 2009 | nil | Mon Mar 23 09:11:46 +1300 2009 | Mon Mar 23 09:11:46 +1300 2009 |
40
+ +----+------------+------------------------+------------------+--------------------------------+--------------------------------+--------------------------------+
41
+
42
+
43
+ # Like to_xml, you can pass a :methods option to add the output methods on your models, and you can pass :only or :except
44
+ # to (respectively) show only certain columns or show all except certain columns:
45
+ >> puts Customer.find(31).purchases.to_table(:only => [:id, :description], :methods => [:met_due_date?])
46
+ +----+------------------------+---------------+
47
+ | id | description | met_due_date? |
48
+ +----+------------------------+---------------+
49
+ | 1 | "Write a handy plugin" | true |
50
+ | 2 | "Blog the plugin" | nil |
51
+ +----+------------------------+---------------+
52
+
53
+ # pt accepts and passes on all options as well:
54
+ >> pt Customer.find(31).purchases, :only => [:id, :description], :methods => [:met_due_date?]
55
+ +----+------------------------+---------------+
56
+ | id | description | met_due_date? |
57
+ +----+------------------------+---------------+
58
+ | 1 | "Write a handy plugin" | true |
59
+ | 2 | "Blog the plugin" | nil |
60
+ +----+------------------------+---------------+
61
+
62
+ # There's a convenient equivalent syntax for displaying an ordered list of columns, like :only and :methods:
63
+ >> puts Customer.find(31).purchases.to_table :id, :description, :met_due_date?
64
+ # which provides:
65
+ >> pt Customer.find(31).purchases, :id, :description, :met_due_date?
66
+ # resulting in the same output as above.
67
+
68
+
69
+ # If :inspect => false is used, the values will be shown in #to_s form rather than #inspect form:
70
+ >> pt Customer.find(31).purchases, :only => [:id, :description, :due_on, :completed_at]
71
+ +----+----------------------+------------+--------------------------------+
72
+ | id | description | due_on | completed_at |
73
+ +----+----------------------+------------+--------------------------------+
74
+ | 1 | Write a handy plugin | 2009-03-25 | Tue Mar 24 23:17:05 +1300 2009 |
75
+ | 2 | Blog the plugin | 2009-04-05 | |
76
+ +----+----------------------+------------+--------------------------------+
77
+
78
+
79
+ # Note that in all cases, values descending from Numeric are right-aligned, while all other values are left-aligned.
80
+
81
+
82
+ Copyright (c) 2009 Will Bryant, Sekuda Ltd, released under the MIT license
data/Rakefile ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env rake
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rake'
5
+ require 'rake/testtask'
6
+
7
+ desc 'Default: run unit tests.'
8
+ task :default => :test
9
+
10
+ desc 'Test the table_display plugin.'
11
+ Rake::TestTask.new(:test) do |t|
12
+ t.libs << 'lib'
13
+ t.libs << 'test'
14
+ t.pattern = 'test/*_test.rb'
15
+ t.verbose = true
16
+ end
data/init.rb ADDED
@@ -0,0 +1,15 @@
1
+ require 'table_display'
2
+
3
+ # all Enumerable classes should get TableDisplay functionality...
4
+ Enumerable.send(:include, TableDisplay)
5
+
6
+ # including those that have already included Enumerable by the time this plugin is loaded.
7
+ # Ruby doesn't recursively update through the module tree, so although any new classes/modules
8
+ # that include Enumerable will get TableDisplay, we have to do it ourself for older ones.
9
+ ObjectSpace.each_object(Module) {|o| o.send(:include, TableDisplay) if o.ancestors.include?(Enumerable)}
10
+ ObjectSpace.each_object(Class) {|o| o.send(:include, TableDisplay) if o.ancestors.include?(Enumerable)}
11
+
12
+ # Rails 2.3 named_scopes certainly quack like enumerables, but surprisingly they don't themself include Enumerable.
13
+ if ActiveRecord.const_defined?(:NamedScope) && ActiveRecord::NamedScope.const_defined?(:Scope)
14
+ ActiveRecord::NamedScope::Scope.send(:include, TableDisplay)
15
+ end
@@ -0,0 +1,105 @@
1
+ module TableDisplay
2
+ def to_table(*args)
3
+ options = args.last.is_a?(Hash) ? args.pop : {}
4
+ extra_methods = args.length > 0 ? args.collect(&:to_s) : []
5
+ extra_methods += Array(options.delete(:methods)) if options[:methods]
6
+ only_attributes = Array(options.delete(:only)) if options[:only]
7
+ only_attributes ||= [] if args.length > 0
8
+ except_attributes = Array(options.delete(:except)) if options[:except]
9
+ except_attributes = (except_attributes + except_attributes.collect(&:to_s)).uniq if except_attributes.present? # we have to keep string and symbol arguments separate for hashes, which may not have 'indifferent access'
10
+ display_inspect = options.nil? || !options.has_key?(:inspect) || options.delete(:inspect)
11
+ raise "unknown options passed to to_table: #{options.keys.to_sentence}" unless options.blank?
12
+
13
+ column_lengths = ActiveSupport::OrderedHash.new
14
+
15
+ data = []
16
+ if only_attributes
17
+ # we've been given an explicit list of attributes to display
18
+ only_attributes.each {|attribute| column_lengths[attribute] = 0}
19
+ else
20
+ # find all the attribute names
21
+ each do |record|
22
+ next if record.nil?
23
+
24
+ # ActiveRecord's #attributes implementation iterates over #attribute_names adding a duped value to the output hash for each entry, so
25
+ # it's actually more expensive to get the keys and values in one go using #attributes than it is for us to work off #attribute_names ourselves.
26
+ if record.respond_to?(:attribute_names)
27
+ # an ActiveModel instance
28
+ attribute_names = record.attribute_names
29
+ elsif Object.const_defined?(:OpenStruct) && record.is_a?(OpenStruct)
30
+ # OpenStruct has a crappy API, #inspect will show the attributes but there's no public way to get a list of them!
31
+ record = record.instance_variable_get("@table")
32
+ attribute_names = record.keys
33
+ elsif record.respond_to?(:attributes)
34
+ # something like an ActiveResource, which doesn't implement attribute_names but does implement attributes
35
+ attribute_names = record.attributes.keys
36
+ else
37
+ # hopefully something like a hash
38
+ attribute_names = record.keys
39
+ end
40
+
41
+ if attribute_names.any? {|name| column_lengths[name].nil?} # optimisation, in most use cases all records will have the same type and the same attributes, so we needn't run this for each - but we do handle varying attribute lists, and attributes that are not columns on the model (calculated columns etc.)
42
+ # for ActiveRecord classes, we look at the .columns explicitly so we can keep them in the right order
43
+ columns_to_check = record.is_a?(ActiveRecord::Base) ? ((record.class.columns.collect(&:name) & attribute_names) + attribute_names).uniq : attribute_names
44
+ columns_to_check.each do |name|
45
+ next if (only_attributes && !only_attributes.include?(name)) || (except_attributes && except_attributes.include?(name))
46
+ column_lengths[name] = 0 # the values of columns are the maximum width of value seen; when we come to print out, if the max seen is zero then the attribute has never actually been seen (eg. when a find(:all, :select => ...) has been used to exclude some of the database columns from the resultset), and we hide the column.
47
+ end
48
+ end
49
+ end
50
+ end
51
+
52
+ # also add any :methods given to the list
53
+ extra_methods.each {|name| column_lengths[name] = 0}
54
+
55
+ each do |record|
56
+ # add the values for all the columns in our list in order they are
57
+ data << column_lengths.collect do |attribute, max_width|
58
+ value = record.is_a?(Hash) ? record[attribute] : record.send(attribute)
59
+ string_value = display_inspect ? value.inspect : (value.is_a?(String) ? value : value.to_s)
60
+ column_lengths[attribute] = string_value.mb_chars.length if string_value.mb_chars.length > max_width
61
+ value.is_a?(Numeric) ? value : string_value # keep Numeric values as-is for now, so we can handle them specially in the output below
62
+ end
63
+ end
64
+
65
+ return [] if data.empty?
66
+
67
+ # build the table header
68
+ separator_string = "+"
69
+ heading_string = "|"
70
+ column_lengths.each do |attribute, max_width|
71
+ next unless max_width > 0 # skip any columns we never actually saw
72
+ name = attribute.to_s
73
+
74
+ # the column needs to fit the column header as well as the values
75
+ if name.mb_chars.length > max_width
76
+ column_lengths[attribute] = max_width = name.mb_chars.length
77
+ end
78
+
79
+ separator_string << '-'*(max_width + 2) << '+'
80
+ heading_string << ' ' << name.ljust(max_width) << ' |'
81
+ end
82
+
83
+ rows = [separator_string, heading_string, separator_string]
84
+ data.each do |data_row|
85
+ data_string = "|"
86
+ column_lengths.each_with_index do |(attribute, max_width), index|
87
+ next unless max_width > 0 # skip any columns we never actually saw
88
+ value = data_row[index]
89
+ if value.is_a?(Numeric)
90
+ data_string << ' ' << (display_inspect ? value.inspect : value.to_s).mb_chars.rjust(max_width) << ' |'
91
+ else
92
+ data_string << ' ' << value.mb_chars.ljust(max_width) << ' |'
93
+ end
94
+ end
95
+ rows << data_string
96
+ end
97
+ rows << separator_string
98
+ end
99
+ end
100
+
101
+ module Kernel
102
+ def pt(target, *options)
103
+ puts target.to_table(*options)
104
+ end
105
+ end
@@ -0,0 +1,3 @@
1
+ module TableDisplay
2
+ VERSION = '0.5.0'
3
+ end
@@ -0,0 +1,38 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/table_display/version', __FILE__)
3
+
4
+ spec = Gem::Specification.new do |gem|
5
+ gem.name = 'table_display'
6
+ gem.version = TableDisplay::VERSION
7
+ gem.summary = "Adds support for displaying your ActiveRecord tables, named scopes, collections, or plain arrays in a table view when working in script/console, shell, or email template."
8
+ gem.description = <<-EOF
9
+ Adds support for displaying your ActiveRecord tables, named scopes, collections, or
10
+ plain arrays in a table view when working in rails console, shell, or email template.
11
+
12
+ Enumerable#to_table returns the printable strings; Object#pt calls #to_table on its
13
+ first argument and puts out the result.
14
+
15
+ Columns you haven't loaded (eg. from using :select) are omitted, and derived/calculated
16
+ columns (eg. again, from using :select) are added.
17
+
18
+ Both #to_table and Object#pt methods take :only, :except, and :methods which work like
19
+ the #to_xml method to change what attributes/methods are output.
20
+
21
+ The normal output uses #inspect on the data values to make them printable, so you can
22
+ see what type the values had. When that's inconvenient or you'd prefer direct display,
23
+ you can pass the option :inspect => false to disable inspection.
24
+ EOF
25
+ gem.has_rdoc = false
26
+ gem.author = "Will Bryant"
27
+ gem.email = "will.bryant@gmail.com"
28
+ gem.homepage = "http://github.com/willbryant/table_display"
29
+
30
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
31
+ gem.files = `git ls-files`.split("\n")
32
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
33
+ gem.require_path = "lib"
34
+
35
+ gem.add_development_dependency "rake"
36
+ gem.add_development_dependency "sqlite3"
37
+ gem.add_development_dependency "activerecord"
38
+ end
data/test/database.yml ADDED
@@ -0,0 +1,3 @@
1
+ test:
2
+ adapter: sqlite3
3
+ database: table_display_test.db
@@ -0,0 +1,4 @@
1
+ this_project:
2
+ id: 31
3
+ name: table_display plugin
4
+ description: "A handy plugin for displaying sets of records in a table format, for easy reading at the console."
@@ -0,0 +1,16 @@
1
+ write_a_handy_plugin:
2
+ id: 1
3
+ project_id: 31
4
+ description: "Write a handy plugin"
5
+ due_on: 2009-03-25
6
+ completed_at: 2009-03-24 23:17:05
7
+ created_at: 2009-03-23 09:11:02
8
+ updated_at: 2009-03-24 23:17:05
9
+
10
+ blog_the_plugin:
11
+ id: 2
12
+ project_id: 31
13
+ description: "Blog the plugin"
14
+ due_on: 2009-04-05
15
+ created_at: 2009-03-23 09:11:46
16
+ updated_at: 2009-03-23 09:11:46
data/test/schema.rb ADDED
@@ -0,0 +1,15 @@
1
+ ActiveRecord::Schema.define(:version => 0) do
2
+ create_table :projects, :force => true do |t|
3
+ t.string :name, :null => false
4
+ t.text :description
5
+ t.timestamps
6
+ end
7
+
8
+ create_table :tasks, :force => true do |t|
9
+ t.integer :project_id, :null => false
10
+ t.string :description, :null => false
11
+ t.date :due_on
12
+ t.datetime :completed_at
13
+ t.timestamps
14
+ end
15
+ end
@@ -0,0 +1,297 @@
1
+ # coding: utf-8
2
+ require File.expand_path(File.join(File.dirname(__FILE__), 'test_helper'))
3
+ require File.expand_path(File.join(File.dirname(__FILE__), 'schema'))
4
+ require 'ostruct'
5
+
6
+ class Time
7
+ def to_s(*args)
8
+ return to_formatted_s(*args) unless args.empty?
9
+ strftime("%Y-%m-%d %H:%M:%S %z")
10
+ end
11
+
12
+ def inspect
13
+ to_s
14
+ end
15
+ end
16
+
17
+ class Project < ActiveRecord::Base
18
+ has_many :tasks
19
+ end
20
+
21
+ class Task < ActiveRecord::Base
22
+ belongs_to :project
23
+
24
+ scope :completed, :conditions => 'completed_at IS NOT NULL'
25
+
26
+ def completed?
27
+ !completed_at.nil?
28
+ end
29
+
30
+ def project_name
31
+ project.name
32
+ end
33
+ end
34
+
35
+ class TableDisplayTest < ActiveSupport::TestCase
36
+ fixtures :all
37
+
38
+ def setup
39
+ @project = projects(:this_project)
40
+ end
41
+
42
+ test "#to_table is available on arrays" do
43
+ assert_nothing_raised do
44
+ [].to_table
45
+ end
46
+ end
47
+
48
+ test "#to_table is available on ActiveRecord find results" do # which should be arrays, in fact
49
+ assert_nothing_raised do
50
+ Task.find(:all).to_table
51
+ end
52
+ end
53
+
54
+ test "#to_table is available on ActiveRecord named_scopes" do
55
+ assert_nothing_raised do
56
+ Task.completed.to_table
57
+ end
58
+ end
59
+
60
+ test "#to_table is available on ActiveRecord association collections" do
61
+ assert_nothing_raised do
62
+ @project.tasks.to_table
63
+ end
64
+ end
65
+
66
+ test "#to_table is available on named scopes in ActiveRecord association collections" do
67
+ assert_nothing_raised do
68
+ @project.tasks.completed.to_table
69
+ end
70
+ end
71
+
72
+ # we run some simple regression tests to check that everything works as expected
73
+
74
+ test "#to_table by default includes all the database columns in database order" do
75
+ assert_equal <<END.strip, @project.tasks.to_table.join("\n")
76
+ +----+------------+------------------------+------------------+---------------------------+---------------------------+---------------------------+
77
+ | id | project_id | description | due_on | completed_at | created_at | updated_at |
78
+ +----+------------+------------------------+------------------+---------------------------+---------------------------+---------------------------+
79
+ | 1 | 31 | "Write a handy plugin" | Wed, 25 Mar 2009 | 2009-03-24 23:17:05 +1300 | 2009-03-23 09:11:02 +1300 | 2009-03-24 23:17:05 +1300 |
80
+ | 2 | 31 | "Blog the plugin" | Sun, 05 Apr 2009 | nil | 2009-03-23 09:11:46 +1300 | 2009-03-23 09:11:46 +1300 |
81
+ +----+------------+------------------------+------------------+---------------------------+---------------------------+---------------------------+
82
+ END
83
+ end
84
+
85
+ test "#to_table by default includes all the database columns in database order even when not called on a typeless array" do
86
+ assert_equal <<END.strip, @project.tasks.find(:all).to_table.join("\n")
87
+ +----+------------+------------------------+------------------+---------------------------+---------------------------+---------------------------+
88
+ | id | project_id | description | due_on | completed_at | created_at | updated_at |
89
+ +----+------------+------------------------+------------------+---------------------------+---------------------------+---------------------------+
90
+ | 1 | 31 | "Write a handy plugin" | Wed, 25 Mar 2009 | 2009-03-24 23:17:05 +1300 | 2009-03-23 09:11:02 +1300 | 2009-03-24 23:17:05 +1300 |
91
+ | 2 | 31 | "Blog the plugin" | Sun, 05 Apr 2009 | nil | 2009-03-23 09:11:46 +1300 | 2009-03-23 09:11:46 +1300 |
92
+ +----+------------+------------------------+------------------+---------------------------+---------------------------+---------------------------+
93
+ END
94
+ end
95
+
96
+ test "#to_table leaves out any attributes not loaded" do
97
+ assert_equal <<END.strip, @project.tasks.find(:all, :select => "id, project_id, completed_at").to_table.join("\n")
98
+ +----+------------+---------------------------+
99
+ | id | project_id | completed_at |
100
+ +----+------------+---------------------------+
101
+ | 1 | 31 | 2009-03-24 23:17:05 +1300 |
102
+ | 2 | 31 | nil |
103
+ +----+------------+---------------------------+
104
+ END
105
+ end
106
+
107
+ test "#to_table also shows any attributes that are not columns on the underlying table" do
108
+ assert_equal <<END.strip, @project.tasks.find(:all, :joins => :project, :select => "tasks.id, project_id, projects.description AS BigProjectDescription").to_table.join("\n")
109
+ +----+------------+-----------------------------------------------------------------------------------------------------+
110
+ | id | project_id | BigProjectDescription |
111
+ +----+------------+-----------------------------------------------------------------------------------------------------+
112
+ | 1 | 31 | "A handy plugin for displaying sets of records in a table format, for easy reading at the console." |
113
+ | 2 | 31 | "A handy plugin for displaying sets of records in a table format, for easy reading at the console." |
114
+ +----+------------+-----------------------------------------------------------------------------------------------------+
115
+ END
116
+ end
117
+
118
+ test "#to_table excludes any columns named in :except" do
119
+ assert_equal <<END.strip, @project.tasks.to_table(:except => ['created_at', :completed_at]).join("\n")
120
+ +----+------------+------------------------+------------------+---------------------------+
121
+ | id | project_id | description | due_on | updated_at |
122
+ +----+------------+------------------------+------------------+---------------------------+
123
+ | 1 | 31 | "Write a handy plugin" | Wed, 25 Mar 2009 | 2009-03-24 23:17:05 +1300 |
124
+ | 2 | 31 | "Blog the plugin" | Sun, 05 Apr 2009 | 2009-03-23 09:11:46 +1300 |
125
+ +----+------------+------------------------+------------------+---------------------------+
126
+ END
127
+ end
128
+
129
+ test "#to_table excludes all columns except those named in :only" do
130
+ assert_equal <<END.strip, @project.tasks.to_table(:only => ['id', :due_on]).join("\n")
131
+ +----+------------------+
132
+ | id | due_on |
133
+ +----+------------------+
134
+ | 1 | Wed, 25 Mar 2009 |
135
+ | 2 | Sun, 05 Apr 2009 |
136
+ +----+------------------+
137
+ END
138
+ end
139
+
140
+ test "#to_table keeps the columns in the order given in :only" do
141
+ assert_equal <<END.strip, @project.tasks.to_table(:only => [:due_on, 'id']).join("\n")
142
+ +------------------+----+
143
+ | due_on | id |
144
+ +------------------+----+
145
+ | Wed, 25 Mar 2009 | 1 |
146
+ | Sun, 05 Apr 2009 | 2 |
147
+ +------------------+----+
148
+ END
149
+ assert_equal <<END.strip, @project.tasks.to_table(:only => [:due_on, :id]).join("\n")
150
+ +------------------+----+
151
+ | due_on | id |
152
+ +------------------+----+
153
+ | Wed, 25 Mar 2009 | 1 |
154
+ | Sun, 05 Apr 2009 | 2 |
155
+ +------------------+----+
156
+ END
157
+ end
158
+
159
+ test "#to_table accepts an unnamed list of arguments for column names" do
160
+ assert_equal <<END.strip, @project.tasks.to_table('id', :due_on, :completed?).join("\n")
161
+ +----+------------------+------------+
162
+ | id | due_on | completed? |
163
+ +----+------------------+------------+
164
+ | 1 | Wed, 25 Mar 2009 | true |
165
+ | 2 | Sun, 05 Apr 2009 | false |
166
+ +----+------------------+------------+
167
+ END
168
+ end
169
+
170
+ test "#to_table allows auxiliary named arguments with the array format" do
171
+ assert_equal <<END.strip, @project.tasks.to_table('id', :due_on, :completed?, :inspect => false).join("\n")
172
+ +----+------------+------------+
173
+ | id | due_on | completed? |
174
+ +----+------------+------------+
175
+ | 1 | 2009-03-25 | true |
176
+ | 2 | 2009-04-05 | false |
177
+ +----+------------+------------+
178
+ END
179
+ end
180
+
181
+ test "#to_table also shows any :methods given as columns" do
182
+ assert_equal <<END.strip, @project.tasks.to_table(:methods => [:completed?, 'project_name']).join("\n")
183
+ +----+------------+------------------------+------------------+---------------------------+---------------------------+---------------------------+------------+------------------------+
184
+ | id | project_id | description | due_on | completed_at | created_at | updated_at | completed? | project_name |
185
+ +----+------------+------------------------+------------------+---------------------------+---------------------------+---------------------------+------------+------------------------+
186
+ | 1 | 31 | "Write a handy plugin" | Wed, 25 Mar 2009 | 2009-03-24 23:17:05 +1300 | 2009-03-23 09:11:02 +1300 | 2009-03-24 23:17:05 +1300 | true | "table_display plugin" |
187
+ | 2 | 31 | "Blog the plugin" | Sun, 05 Apr 2009 | nil | 2009-03-23 09:11:46 +1300 | 2009-03-23 09:11:46 +1300 | false | "table_display plugin" |
188
+ +----+------------+------------------------+------------------+---------------------------+---------------------------+---------------------------+------------+------------------------+
189
+ END
190
+ end
191
+
192
+ test "#to_table shows the #to_s format rather than the #inspect format when :inspect => false is set" do
193
+ assert_equal <<END.strip, @project.tasks.to_table(:inspect => false).join("\n")
194
+ +----+------------+----------------------+------------+---------------------------+---------------------------+---------------------------+
195
+ | id | project_id | description | due_on | completed_at | created_at | updated_at |
196
+ +----+------------+----------------------+------------+---------------------------+---------------------------+---------------------------+
197
+ | 1 | 31 | Write a handy plugin | 2009-03-25 | 2009-03-24 23:17:05 +1300 | 2009-03-23 09:11:02 +1300 | 2009-03-24 23:17:05 +1300 |
198
+ | 2 | 31 | Blog the plugin | 2009-04-05 | | 2009-03-23 09:11:46 +1300 | 2009-03-23 09:11:46 +1300 |
199
+ +----+------------+----------------------+------------+---------------------------+---------------------------+---------------------------+
200
+ END
201
+ # note the strings no longer have quotes, the nil is not shown, and the date format happens to be different
202
+ end
203
+
204
+ test "#to_table correctly pads out to match the length in characters of long values with utf-8 sequences" do
205
+ tasks(:write_a_handy_plugin).update_attribute(:description, "Write a handy plugin \342\200\223 with UTF-8 handling")
206
+ assert_equal <<END.strip, @project.tasks.to_table(:only => [:id, :description], :inspect => false).join("\n")
207
+ +----+--------------------------------------------+
208
+ | id | description |
209
+ +----+--------------------------------------------+
210
+ | 1 | Write a handy plugin – with UTF-8 handling |
211
+ | 2 | Blog the plugin |
212
+ +----+--------------------------------------------+
213
+ END
214
+ end
215
+
216
+ test "#to_table correctly pads out short values with utf-8 sequences" do
217
+ tasks(:blog_the_plugin).update_attribute(:description, "Blog \342\200\223 plugin")
218
+ assert_equal <<END.strip, @project.tasks.to_table(:only => [:id, :description], :inspect => false).join("\n")
219
+ +----+----------------------+
220
+ | id | description |
221
+ +----+----------------------+
222
+ | 1 | Write a handy plugin |
223
+ | 2 | Blog – plugin |
224
+ +----+----------------------+
225
+ END
226
+ end
227
+
228
+ test "#to_table on an empty array returns an empty result" do
229
+ assert_equal [], [].to_table
230
+ end
231
+
232
+ test "#to_table can extract data out of raw hashes" do
233
+ @records = [{:foo => 1234, :bar => "test"},
234
+ {:bar => "text", :baz => 5678}]
235
+ results = @records.to_table.join("\n")
236
+ assert results.include?('| foo |')
237
+ assert results.include?('| bar |')
238
+ assert results.include?('| baz |')
239
+ assert_equal <<END.strip, @records.to_table(:only => [:foo, :bar, :baz]).join("\n")
240
+ +------+--------+------+
241
+ | foo | bar | baz |
242
+ +------+--------+------+
243
+ | 1234 | "test" | nil |
244
+ | nil | "text" | 5678 |
245
+ +------+--------+------+
246
+ END
247
+ assert_equal <<END.strip, @records.to_table(:only => [:bar, :baz]).join("\n")
248
+ +--------+------+
249
+ | bar | baz |
250
+ +--------+------+
251
+ | "test" | nil |
252
+ | "text" | 5678 |
253
+ +--------+------+
254
+ END
255
+ assert_equal <<END.strip, @records.to_table(:except => [:bar]).join("\n")
256
+ +------+------+
257
+ | foo | baz |
258
+ +------+------+
259
+ | 1234 | nil |
260
+ | nil | 5678 |
261
+ +------+------+
262
+ END
263
+ end
264
+
265
+ test "#to_table can extract data out of OpenStruct records" do
266
+ @records = [OpenStruct.new(:foo => 1234, :bar => "test"),
267
+ OpenStruct.new(:bar => "text", :baz => 5678)]
268
+ results = @records.to_table.join("\n")
269
+ assert results.include?('| foo |')
270
+ assert results.include?('| bar |')
271
+ assert results.include?('| baz |')
272
+ assert_equal <<END.strip, @records.to_table(:only => [:foo, :bar, :baz]).join("\n")
273
+ +------+--------+------+
274
+ | foo | bar | baz |
275
+ +------+--------+------+
276
+ | 1234 | "test" | nil |
277
+ | nil | "text" | 5678 |
278
+ +------+--------+------+
279
+ END
280
+ assert_equal <<END.strip, @records.to_table(:only => [:bar, :baz]).join("\n")
281
+ +--------+------+
282
+ | bar | baz |
283
+ +--------+------+
284
+ | "test" | nil |
285
+ | "text" | 5678 |
286
+ +--------+------+
287
+ END
288
+ assert_equal <<END.strip, @records.to_table(:except => [:bar]).join("\n")
289
+ +------+------+
290
+ | foo | baz |
291
+ +------+------+
292
+ | 1234 | nil |
293
+ | nil | 5678 |
294
+ +------+------+
295
+ END
296
+ end
297
+ end
@@ -0,0 +1,28 @@
1
+ require 'rubygems'
2
+ require "../../../config/boot.rb" if File.exist?("../../../config/boot.rb")
3
+
4
+ $KCODE = 'u' if RUBY_VERSION < '1.9' # as per railties initializer.rb: set unicode mode for Ruby 1.8 (Ruby 1.9 just works)
5
+
6
+ require 'test/unit'
7
+ require 'active_support'
8
+ require 'active_support/test_case'
9
+ require 'active_record'
10
+ require 'active_record/fixtures'
11
+
12
+ begin
13
+ require 'ruby-debug'
14
+ Debugger.start
15
+ rescue LoadError
16
+ # ruby-debug not installed, no debugging for you
17
+ end
18
+
19
+ ENV['RAILS_ENV'] ||= 'test'
20
+ FileUtils.mkdir File.join(File.dirname(__FILE__), "log") rescue nil
21
+ RAILS_DEFAULT_LOGGER = ActiveRecord::Base.logger = Logger.new(File.join(File.dirname(__FILE__), "log", "#{ENV['RAILS_ENV']}.log"))
22
+
23
+ ActiveRecord::Base.configurations = YAML::load(IO.read(File.join(File.dirname(__FILE__), "database.yml")))
24
+ ActiveRecord::Base.establish_connection ActiveRecord::Base.configurations[ENV['RAILS_ENV']]
25
+ ActiveSupport::TestCase.send(:include, ActiveRecord::TestFixtures) if ActiveRecord.const_defined?('TestFixtures')
26
+ ActiveSupport::TestCase.fixture_path = File.join(File.dirname(__FILE__), "fixtures")
27
+
28
+ require File.expand_path(File.join(File.dirname(__FILE__), '../init')) # load table_display
metadata ADDED
@@ -0,0 +1,141 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: table_display
3
+ version: !ruby/object:Gem::Version
4
+ hash: 11
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 5
9
+ - 0
10
+ version: 0.5.0
11
+ platform: ruby
12
+ authors:
13
+ - Will Bryant
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-08-18 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: rake
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ hash: 3
29
+ segments:
30
+ - 0
31
+ version: "0"
32
+ type: :development
33
+ version_requirements: *id001
34
+ - !ruby/object:Gem::Dependency
35
+ name: sqlite3
36
+ prerelease: false
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ none: false
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ hash: 3
43
+ segments:
44
+ - 0
45
+ version: "0"
46
+ type: :development
47
+ version_requirements: *id002
48
+ - !ruby/object:Gem::Dependency
49
+ name: activerecord
50
+ prerelease: false
51
+ requirement: &id003 !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ hash: 3
57
+ segments:
58
+ - 0
59
+ version: "0"
60
+ type: :development
61
+ version_requirements: *id003
62
+ description: |
63
+ Adds support for displaying your ActiveRecord tables, named scopes, collections, or
64
+ plain arrays in a table view when working in rails console, shell, or email template.
65
+
66
+ Enumerable#to_table returns the printable strings; Object#pt calls #to_table on its
67
+ first argument and puts out the result.
68
+
69
+ Columns you haven't loaded (eg. from using :select) are omitted, and derived/calculated
70
+ columns (eg. again, from using :select) are added.
71
+
72
+ Both #to_table and Object#pt methods take :only, :except, and :methods which work like
73
+ the #to_xml method to change what attributes/methods are output.
74
+
75
+ The normal output uses #inspect on the data values to make them printable, so you can
76
+ see what type the values had. When that's inconvenient or you'd prefer direct display,
77
+ you can pass the option :inspect => false to disable inspection.
78
+
79
+ email: will.bryant@gmail.com
80
+ executables: []
81
+
82
+ extensions: []
83
+
84
+ extra_rdoc_files: []
85
+
86
+ files:
87
+ - .gitignore
88
+ - Gemfile
89
+ - MIT-LICENSE
90
+ - README
91
+ - Rakefile
92
+ - init.rb
93
+ - lib/table_display.rb
94
+ - lib/table_display/version.rb
95
+ - table_display.gemspec
96
+ - test/database.yml
97
+ - test/fixtures/projects.yml
98
+ - test/fixtures/tasks.yml
99
+ - test/schema.rb
100
+ - test/table_display_test.rb
101
+ - test/test_helper.rb
102
+ homepage: http://github.com/willbryant/table_display
103
+ licenses: []
104
+
105
+ post_install_message:
106
+ rdoc_options: []
107
+
108
+ require_paths:
109
+ - lib
110
+ required_ruby_version: !ruby/object:Gem::Requirement
111
+ none: false
112
+ requirements:
113
+ - - ">="
114
+ - !ruby/object:Gem::Version
115
+ hash: 3
116
+ segments:
117
+ - 0
118
+ version: "0"
119
+ required_rubygems_version: !ruby/object:Gem::Requirement
120
+ none: false
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ hash: 3
125
+ segments:
126
+ - 0
127
+ version: "0"
128
+ requirements: []
129
+
130
+ rubyforge_project:
131
+ rubygems_version: 1.8.15
132
+ signing_key:
133
+ specification_version: 3
134
+ summary: Adds support for displaying your ActiveRecord tables, named scopes, collections, or plain arrays in a table view when working in script/console, shell, or email template.
135
+ test_files:
136
+ - test/database.yml
137
+ - test/fixtures/projects.yml
138
+ - test/fixtures/tasks.yml
139
+ - test/schema.rb
140
+ - test/table_display_test.rb
141
+ - test/test_helper.rb