table_display 0.5.0

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