table_print-1.5.7/ 0000755 0000041 0000041 00000000000 13701574164 014067 5 ustar www-data www-data table_print-1.5.7/.travis.yml 0000644 0000041 0000041 00000000127 13701574164 016200 0 ustar www-data www-data language: ruby
rvm:
- 2.4
- 2.5
- 2.6
- 2.7
- jruby-18mode
- jruby-19mode
table_print-1.5.7/.rspec 0000644 0000041 0000041 00000000010 13701574164 015173 0 ustar www-data www-data --color
table_print-1.5.7/spec/ 0000755 0000041 0000041 00000000000 13701574164 015021 5 ustar www-data www-data table_print-1.5.7/spec/printable_spec.rb 0000644 0000041 0000041 00000003254 13701574164 020344 0 ustar www-data www-data require 'spec_helper'
describe TablePrint::Printable do
before(:each) do
Sandbox.cleanup!
end
describe "#default_display_methods" do
it "returns attribute getters" do
Sandbox.add_class("Hat")
Sandbox.add_attributes("Hat", "brand")
p = Sandbox::Hat.new
TablePrint::Printable.default_display_methods(p).should == %W(brand)
end
it "ignores dangerous methods" do
Sandbox.add_class("Hat")
Sandbox.add_method("Hat", "brand!") {}
p = Sandbox::Hat.new
TablePrint::Printable.default_display_methods(p).should == []
end
it "ignores methods defined in a superclass" do
Sandbox.add_class("Hat::Bowler")
Sandbox.add_attributes("Hat", "brand")
Sandbox.add_attributes("Hat::Bowler", "brim_width")
p = Sandbox::Hat::Bowler.new
TablePrint::Printable.default_display_methods(p).should == %W(brim_width)
end
it "ignores methods that require arguments" do
Sandbox.add_class("Hat")
Sandbox.add_attributes("Hat", "brand")
Sandbox.add_method("Hat", "tip?") { |person| person.rapscallion? }
p = Sandbox::Hat.new
TablePrint::Printable.default_display_methods(p).should == %W(brand)
end
it "ignores methods from an included module" do
pending "waiting for Cat to support module manipulation"
end
it "uses column information when available (eg, from ActiveRecord objects)"
it "uses the members method when passed a Struct" do
test_struct = Struct.new(:foo, :bar)
obj = test_struct.new
obj.foo = 1
obj.bar = 2
TablePrint::Printable.default_display_methods(obj).should == [:foo, :bar]
end
end
end
table_print-1.5.7/spec/fingerprinter_spec.rb 0000644 0000041 0000041 00000017627 13701574164 021253 0 ustar www-data www-data require 'spec_helper'
class TablePrint::Row
attr_accessor :groups, :cells
end
include TablePrint
describe Fingerprinter do
before(:each) do
Sandbox.cleanup!
end
describe "#lift" do
it "turns a single level of columns into a single row" do
rows = Fingerprinter.new.lift([Column.new(:name => "name")], OpenStruct.new(:name => "dale carnegie"))
rows.length.should == 1
row = rows.first
row.children.length.should == 0
row.cells.should == {'name' => "dale carnegie"}
end
it "uses the display_method to get the data" do
rows = Fingerprinter.new.lift([Column.new(:name => "name of work", :display_method => "title")], OpenStruct.new(:title => "of mice and men"))
rows.length.should == 1
row = rows.first
row.children.length.should == 0
row.cells.should == {'name of work' => "of mice and men"}
end
it "turns multiple levels of columns into multiple rows" do
rows = Fingerprinter.new.lift([Column.new(:name => "name"), Column.new(:name => "books.title")], OpenStruct.new(:name => "dale carnegie", :books => [OpenStruct.new(:title => "how to make influences")]))
rows.length.should == 1
row = rows.first
row.children.length.should == 1
row.cells.should == {'name' => "dale carnegie"}
row.children.first.children.first.cells.should == {'books.title' => "how to make influences"}
end
it "doesn't choke if an association doesn't exist" do
rows = Fingerprinter.new.lift([Column.new(:name => "name"), Column.new(:name => "books.title")], OpenStruct.new(:name => "dale carnegie", :books => []))
rows.length.should == 1
row = rows.first
row.children.length.should == 0
end
it "allows a lambda as the display_method" do
rows = Fingerprinter.new.lift([Column.new(:name => "name", :display_method => lambda { |row| row.name.gsub(/[aeiou]/, "") })], OpenStruct.new(:name => "dale carnegie"))
rows.length.should == 1
row = rows.first
row.children.length.should == 0
row.cells.should == {'name' => "dl crng"}
end
it "doesn't puke if a lambda returns nil" do
rows = Fingerprinter.new.lift([Column.new(:name => "name", :display_method => lambda { |row| nil })], OpenStruct.new(:name => "dale carnegie"))
rows.length.should == 1
row = rows.first
row.children.length.should == 0
row.cells.should == {'name' => nil}
end
end
describe "#hash_to_rows" do
it "uses hashes with empty values as column names" do
f = Fingerprinter.new
f.instance_variable_set("@column_names_by_display_method", {"name" => "name"})
rows = f.hash_to_rows("", {'name' => {}}, OpenStruct.new(:name => "dale carnegie"))
rows.length.should == 1
row = rows.first
row.children.length.should == 0
row.cells.should == {'name' => 'dale carnegie'}
end
it 'recurses for subsequent levels of hash' do
f = Fingerprinter.new
f.instance_variable_set("@column_names_by_display_method", {"name" => "name", "books.title" => "books.title"})
rows = f.hash_to_rows("", {'name' => {}, 'books' => {'title' => {}}}, [OpenStruct.new(:name => 'dale carnegie', :books => [OpenStruct.new(:title => "hallmark")])])
rows.length.should == 1
top_row = rows.first
top_row.cells.should == {'name' => 'dale carnegie'}
top_row.children.length.should == 1
top_row.children.first.child_count.should == 1
bottom_row = top_row.children.first.children.first
bottom_row.cells.should == {'books.title' => 'hallmark'}
end
end
describe "#populate_row" do
it "fills a row by calling methods on the target object" do
f = Fingerprinter.new
f.instance_variable_set("@column_names_by_display_method", {"title" => "title", "author" => "author"})
row = f.populate_row("", {'title' => {}, 'author' => {}, 'publisher' => {'address' => {}}}, OpenStruct.new(:title => "foobar", :author => "bobby"))
row.cells.should == {'title' => "foobar", 'author' => 'bobby'}
end
it "uses the provided prefix to name the cells" do
f = Fingerprinter.new
f.instance_variable_set("@column_names_by_display_method", {"bar.title" => "bar.title", "bar.author" => "bar.author"})
row = f.populate_row("bar", {'title' => {}, 'author' => {}, 'publisher' => {'address' => {}}}, OpenStruct.new(:title => "foobar", :author => "bobby"))
row.cells.should == {'bar.title' => "foobar", 'bar.author' => 'bobby'}
end
it "uses the column name as the cell name but uses the display method to get the value" do
f = Fingerprinter.new
f.instance_variable_set("@column_names_by_display_method", {"bar.title" => "title", "bar.author" => "bar.author"})
row = f.populate_row("bar", {'title' => {}, 'author' => {}, 'publisher' => {'address' => {}}}, OpenStruct.new(:title => "foobar", :author => "bobby"))
row.cells.should == {'title' => "foobar", 'bar.author' => 'bobby'}
end
context 'using a hash as input_data' do
it "fills a row by calling methods on the target object" do
f = Fingerprinter.new
f.instance_variable_set('@column_names_by_display_method', {'title' => 'title', 'author' => 'author'})
input_data = {:title => 'foobar', :author => 'bobby'}
row = f.populate_row('', {'title' => {}, 'author' => {}, 'publisher' => {'address' => {}}}, input_data)
row.cells.should == {'title' => 'foobar', 'author' => 'bobby'}
end
it "fills a row by calling methods on the target object" do
f = Fingerprinter.new
f.instance_variable_set('@column_names_by_display_method', {'title' => 'title', 'author' => 'author'})
input_data = {'title' => 'foobar', 'author' => 'bobby'}
row = f.populate_row('', {'title' => {}, 'author' => {}, 'publisher' => {'address' => {}}}, input_data)
row.cells.should == {'title' => 'foobar', 'author' => 'bobby'}
end
end
context "when the method isn't found" do
it "sets the cell value to an error string" do
f = Fingerprinter.new
f.instance_variable_set('@column_names_by_display_method', {'foo' => 'foo'})
row = f.populate_row('', {'foo' => {}}, Hash.new)
row.cells.should == {'foo' => 'Method Missing'}
end
end
end
describe "#create_child_group" do
it "adds the next level of column information to the prefix" do
f = Fingerprinter.new
books = []
f.should_receive(:hash_to_rows).with("author.books", {'title' => {}}, books).and_return([])
groups = f.create_child_group("author", {'books' => {'title' => {}}}, OpenStruct.new(:name => "bobby", :books => books))
groups.length.should == 1
groups.first.should be_a TablePrint::RowGroup
end
end
describe "#columns_to_handle" do
it "returns hash keys that have an empty hash as the value" do
Fingerprinter.new.handleable_columns({'name' => {}, 'books' => {'title' => {}}}).should == ["name"]
end
end
describe "#columns_to_pass" do
it "returns hash keys that do not have an empty hash as the value" do
Fingerprinter.new.passable_columns({'name' => {}, 'books' => {'title' => {}}}).should == ["books"]
end
end
describe "#chain_to_nested_hash" do
it "turns a list of methods into a nested hash" do
Fingerprinter.new.display_method_to_nested_hash("books").should == {'books' => {}}
Fingerprinter.new.display_method_to_nested_hash("reviews.user").should == {'reviews' => {'user' => {}}}
end
end
describe "#columns_to_nested_hash" do
it "splits the column names into a nested hash" do
Fingerprinter.new.display_methods_to_nested_hash(["books.name"]).should == {'books' => {'name' => {}}}
Fingerprinter.new.display_methods_to_nested_hash(
["books.name", "books.publisher", "reviews.rating", "reviews.user.email", "reviews.user.id"]
).should == {'books' => {'name' => {}, 'publisher' => {}}, 'reviews' => {'rating' => {}, 'user' => {'email' => {}, 'id' => {}}}}
end
end
end
table_print-1.5.7/spec/config_spec.rb 0000644 0000041 0000041 00000004054 13701574164 017630 0 ustar www-data www-data require 'spec_helper'
describe TablePrint::Config do
it "defaults max_width to 30" do
TablePrint::Config.max_width.should == 30
end
it "defaults time_format to year-month-day-hour-minute-second" do
TablePrint::Config.time_format.should == "%Y-%m-%d %H:%M:%S"
end
describe "individual config options" do
describe "storing and retrieving" do
it "sets the variable" do
TablePrint::Config.set(:max_width, [10])
TablePrint::Config.max_width.should == 10
TablePrint::Config.set(:max_width, [30])
end
end
describe "clearing" do
it "resets the variable to its initial value" do
TablePrint::Config.set(:max_width, [10])
TablePrint::Config.clear(:max_width)
TablePrint::Config.max_width.should == 30
end
end
end
describe "class-based column config" do
before :all do
Sandbox.add_class("Blog")
end
describe "storing and retrieving" do
it "stores the column hash" do
TablePrint::Config.set(Sandbox::Blog, [:title, :author])
TablePrint::Config.for(Sandbox::Blog).should == [:title, :author]
end
end
describe "clearing" do
it "removes the class from storage" do
TablePrint::Config.set(Sandbox::Blog, [:title, :author])
TablePrint::Config.clear(Sandbox::Blog)
TablePrint::Config.for(Sandbox::Blog).should be_nil
end
end
end
describe "io" do
before :all do
Sandbox.add_class("MyIO")
Sandbox.add_method("MyIO", :puts) {}
end
it "accepts object that respond to puts" do
myIO = Sandbox::MyIO.new
TablePrint::Config.set(:io, [myIO])
TablePrint::Config.io.should == myIO
end
it "doesn't accept objects unless they respond to puts" do
lambda {
TablePrint::Config.set(:io, [""])
}.should raise_error StandardError
end
it "defaults to STDOUT" do
myIO = Sandbox::MyIO.new
TablePrint::Config.set(:io, [myIO])
TablePrint::Config.clear(:io)
TablePrint::Config.io.should == STDOUT
end
end
end
table_print-1.5.7/spec/table_print_spec.rb 0000644 0000041 0000041 00000006350 13701574164 020667 0 ustar www-data www-data require 'spec_helper'
require 'table_print'
include TablePrint
describe TablePrint::Printer do
before(:each) do
Sandbox.cleanup!
end
describe "printing an empty array" do
it "returns the string 'no data'" do
p = Printer.new([])
p.table_print.should == 'No data.'
end
end
describe "printing an object where there are only association columns with no data" do
it "returns the string 'no data'" do
Sandbox.add_class("Blog")
Sandbox.add_attributes("Blog", :author)
p = Printer.new(Sandbox::Blog.new, "author.name")
p.table_print.should == 'No data.'
end
end
describe "message" do
it "defaults to the time the print took, but in string" do
message = Printer.new([]).message
message.should be_a String
expect(message.to_f.to_s).to eq(message)
end
it "shows a warning if the printed objects have config" do
Sandbox.add_class("User")
tp.set Sandbox::User, :id, :email
p = Printer.new(Sandbox::User.new)
p.message.should == "Printed with config"
end
end
describe "#columns" do
it "pulls the column names off the data object" do
Sandbox.add_class("Post")
Sandbox.add_attributes("Post", :title)
p = Printer.new(Sandbox::Post.new)
cols = p.send(:columns)
cols.length.should == 1
cols.first.name.should == 'title'
end
it 'pull the column names off of the array of Structs' do
struct = Struct.new(:name, :surname)
data = [struct.new("User 1", "Familyname 1"), struct.new("User 2", "Familyname 2")]
p = Printer.new(data)
cols = p.send(:columns)
cols.length.should == 2
cols.collect(&:name).sort.should == ['name', 'surname']
end
context 'when keys are symbols' do
it "pulls the column names off the array of hashes" do
data = [{:name => "User 1",
:surname => "Familyname 1"
},
{:name => "User 2",
:surname => "Familyname 2"}]
p = Printer.new(data)
cols = p.send(:columns)
cols.length.should == 2
cols.collect(&:name).sort.should == ['name', 'surname']
end
end
context 'when keys are strings' do
it "pulls the column names off the array of hashes" do
data = [{'name' => "User 1",
'surname' => "Familyname 1"
},
{'name' => "User 2",
'surname' => "Familyname 2"}]
p = Printer.new(data)
cols = p.send(:columns)
cols.length.should == 2
cols.collect(&:name).sort.should == ['name', 'surname']
end
end
it "pulls out excepted columns" do
Sandbox.add_class("Post")
Sandbox.add_attributes("Post", :title, :author)
p = Printer.new(Sandbox::Post.new, :except => :title)
cols = p.send(:columns)
cols.length.should == 1
cols.first.name.should == 'author'
end
it "adds included columns" do
Sandbox.add_class("Post")
Sandbox.add_attributes("Post", :title)
p = Printer.new(Sandbox::Post.new, :include => :author)
cols = p.send(:columns)
cols.length.should == 2
cols.first.name.should == 'title'
cols.last.name.should == 'author'
end
end
end
table_print-1.5.7/spec/returnable_spec.rb 0000644 0000041 0000041 00000001656 13701574164 020533 0 ustar www-data www-data require 'spec_helper'
describe TablePrint::Returnable do
it "returns its initialized value from its to_s method" do
r = TablePrint::Returnable.new("foobar")
r.to_s.should == "foobar"
end
it "returns its initialized value from its inspect method" do
# 1.8.7 calls inspect on return values
r = TablePrint::Returnable.new("foobar")
r.inspect.should == "foobar"
end
it "passes #set through to TablePrint::Config" do
TablePrint::Config.should_receive(:set).with(Object, [:foo])
r = TablePrint::Returnable.new
r.set(Object, :foo)
end
it "passes #clear through to TablePrint::Config" do
TablePrint::Config.should_receive(:clear).with(Object)
r = TablePrint::Returnable.new
r.clear(Object)
end
it "passes #config_for through to TablePrint::Config.for" do
TablePrint::Config.should_receive(:for).with(Object)
r = TablePrint::Returnable.new
r.config_for(Object)
end
end
table_print-1.5.7/spec/hash_extensions_spec.rb 0000644 0000041 0000041 00000001225 13701574164 021562 0 ustar www-data www-data require 'spec_helper'
describe "#constructive_merge" do
it "merges hashes without clobbering" do
x = {'reviews' => {'user' => {}}}
y = {'reviews' => {'ratings' => {}}}
x.extend TablePrint::HashExtensions::ConstructiveMerge
x.constructive_merge(y).should == {'reviews' => {'user' => {}, 'ratings' => {}}}
end
end
describe "#constructive_merge!" do
it "merges hashes in place without clobbering" do
x = {'reviews' => {'user' => {}}}
y = {'reviews' => {'ratings' => {}}}
x.extend TablePrint::HashExtensions::ConstructiveMerge
x.constructive_merge!(y)
x.should == {'reviews' => {'user' => {}, 'ratings' => {}}}
end
end
table_print-1.5.7/spec/config_resolver_spec.rb 0000644 0000041 0000041 00000021336 13701574164 021553 0 ustar www-data www-data require 'spec_helper'
describe TablePrint::ConfigResolver do
#it "starts with the specified config" do
# Sandbox.add_class("Configged")
# TablePrint::Config.set(Sandbox::Configged, [:title, :author])
# c = TablePrint::ConfigResolver.new(Object, Object, [:name])
# c.columns.length.should == 2
# c.columns.first.name.should == 'title'
# c.columns.last.name.should == 'author'
#end
describe "#get_and_remove" do
it "deletes and returns the :except key from an array" do
c = TablePrint::ConfigResolver.new(Object, [])
options = [:title, :author, {:except => [:title]}]
c.get_and_remove(options, :except).should == [:title]
options.should == [:title, :author]
end
it "deletes and returns the :except key from an array with an :include key" do
c = TablePrint::ConfigResolver.new(Object, [])
options = [:title, {:except => [:title]}, {:include => [:author]}]
c.get_and_remove(options, :except).should == [:title]
options.should == [:title, {:include => [:author]}]
end
it "deletes and returns the :except key from a hash with an :include key" do
c = TablePrint::ConfigResolver.new(Object, [])
options = [:title, {:except => [:title], :include => [:author]}]
c.get_and_remove(options, :except).should == [:title]
options.should == [:title, {:include => [:author]}]
end
it "deletes and returns both the :include and :except keys" do
c = TablePrint::ConfigResolver.new(Object, [])
options = [:title, {:except => [:title]}, {:include => [:author]}]
c.get_and_remove(options, :include).should == [:author]
c.get_and_remove(options, :except).should == [:title]
options.should == [:title]
end
it "works even if the array doesn't have an exception hash" do
c = TablePrint::ConfigResolver.new(Object, [])
options = [:title, :author]
c.get_and_remove(options, :except).should == []
options.should == [:title, :author]
end
end
describe ":only" do
context "with a symbol" do
it "returns a column named foo" do
c = TablePrint::ConfigResolver.new(Object, [:title], :foo)
c.columns.length.should == 1
c.columns.first.name.should == 'foo'
end
end
context "with a string" do
it "returns a column named foo" do
c = TablePrint::ConfigResolver.new(Object, [:title], 'foo')
c.columns.length.should == 1
c.columns.first.name.should == 'foo'
end
end
context "with an array of symbols and strings" do
it "returns columns named foo and bar" do
c = TablePrint::ConfigResolver.new(Object, [:title], :foo, 'bar')
c.columns.length.should == 2
c.columns.first.name.should == 'foo'
c.columns.last.name.should == 'bar'
end
end
end
describe ":include" do
context "with a symbol" do
it "adds foo to the list of methods" do
c = TablePrint::ConfigResolver.new(Object, [:title], :include => :foo)
c.columns.length.should == 2
c.columns.first.name.should == 'title'
c.columns.last.name.should == 'foo'
end
end
context "with an array" do
it "adds foo and bar to the list of methods" do
c = TablePrint::ConfigResolver.new(Object, [:title], :include => [:foo, :bar])
c.columns.length.should == 3
c.columns.first.name.should == 'title'
c.columns.last.name.should == 'bar'
end
end
context "with options" do
it "adds foo to the list of methods and remembers its options" do
c = TablePrint::ConfigResolver.new(Object, [:title], :include => {:foo => {:width => 10}})
c.columns.length.should == 2
c.columns.first.name.should == 'title'
c.columns.last.name.should == 'foo'
c.columns.last.default_width.should == 10
end
end
end
describe ":except" do
context "with a symbol" do
it "removes foo from the list of methods" do
c = TablePrint::ConfigResolver.new(Object, [:title, :foo], :except => :foo)
c.columns.length.should == 1
c.columns.first.name.should == 'title'
end
end
context "with an array" do
it "removes foo and bar from the list of methods" do
c = TablePrint::ConfigResolver.new(Object, [:title, :foo, :bar], :except => [:foo, 'bar'])
c.columns.length.should == 1
c.columns.first.name.should == 'title'
end
end
end
describe "lambdas" do
it "uses the key as the name and the lambda as the display method" do
lam = lambda {}
c = TablePrint::ConfigResolver.new(Object, [:title], :foo => {:display_method => lam})
c.columns.length.should == 1
c.columns.first.name.should == 'foo'
c.columns.first.display_method.should == lam
end
context "without the display_method keyword" do
it "uses the key as the name and the lambda as the display method" do
lam = lambda {}
c = TablePrint::ConfigResolver.new(Object, [:title], :foo => lam)
c.columns.length.should == 1
c.columns.first.name.should == 'foo'
c.columns.first.display_method.should == lam
end
end
end
describe "#usable_column_names" do
it "returns default columns" do
c = TablePrint::ConfigResolver.new(Object, [:title])
c.usable_column_names.should == ['title']
end
it "returns specified columns instead of default columns" do
c = TablePrint::ConfigResolver.new(Object, [:title], [:author])
c.usable_column_names.should == ['author']
end
it "applies includes on top of default columns" do
c = TablePrint::ConfigResolver.new(Object, [:title], [:include => :author])
c.usable_column_names.should == ['title', 'author']
end
it "applies includes on top of specified columns" do
c = TablePrint::ConfigResolver.new(Object, [:title], [:author, {:include => :pub_date}])
c.usable_column_names.should == ['author', 'pub_date']
end
it "applies excepts on top of default columns" do
c = TablePrint::ConfigResolver.new(Object, [:title, :author], [:except => :author])
c.usable_column_names.should == ['title']
end
it "applies excepts on top of specified columns" do
c = TablePrint::ConfigResolver.new(Object, [:title, :author], [:pub_date, :length, {:except => :length}])
c.usable_column_names.should == ['pub_date']
end
it "applies both includes and excepts on top of specified columns" do
c = TablePrint::ConfigResolver.new(Object, [:title, :author], [:pub_date, :length, {:except => :length, :include => :foobar}])
c.usable_column_names.should == ['pub_date', 'foobar']
end
end
describe "column options" do
context "display_method" do
it "sets the display method on the column" do
c = TablePrint::ConfigResolver.new(Object, [:title], :title => {:display_method => :boofar})
c.columns.length.should == 1
c.columns.first.name.should == 'title'
c.columns.first.display_method.should == "boofar"
end
end
context "width" do
it "sets the default width" do
c = TablePrint::ConfigResolver.new(Object, [:title], :title => {:width => 100})
c.columns.length.should == 1
c.columns.first.name.should == 'title'
c.columns.first.default_width.should == 100
end
end
context "formatters" do
it "adds the formatters to the column" do
f1 = {}
f2 = {}
c = TablePrint::ConfigResolver.new(Object, [:title], :title => {:formatters => [f1, f2]})
c.columns.length.should == 1
c.columns.first.name.should == 'title'
c.columns.first.formatters.should == [f1, f2]
end
end
context "display_name" do
it "sets the display name on the column" do
c = TablePrint::ConfigResolver.new(Object, [], :title => {:display_name => "Ti Tle"})
c.columns.length.should == 1
c.columns.first.name.should == 'Ti Tle'
c.columns.first.display_method.should == "title"
end
end
end
describe "#option_to_column" do
context "with a symbol" do
it "returns a column named foo" do
c = TablePrint::ConfigResolver.new(Object, [])
column = c.option_to_column(:foo)
column.name.should == 'foo'
end
end
context "with a string" do
it "returns a column named foo" do
c = TablePrint::ConfigResolver.new(Object, [])
column = c.option_to_column('foo')
column.name.should == 'foo'
end
end
context "with a hash" do
it "returns a column named foo and the specified options" do
c = TablePrint::ConfigResolver.new(Object, [])
column = c.option_to_column({:foo => {:default_width => 10}})
column.name.should == 'foo'
column.default_width.should == 10
end
end
end
end
table_print-1.5.7/spec/formatter_spec.rb 0000644 0000041 0000041 00000003223 13701574164 020363 0 ustar www-data www-data require 'spec_helper'
include TablePrint
describe TablePrint::TimeFormatter do
describe "#format" do
it "only operates on Time objects" do
f = TablePrint::TimeFormatter.new
f.format(12).should == 12
end
it "uses the config'd time_format" do
f = TablePrint::TimeFormatter.new
time = Time.local(2012, 01, 11, 1, 23, 45)
f.format(time).should == "2012-01-11 01:23:45" # default time format is set in config.rb
end
it "overrides the config'd time format with one it was passed" do
f = TablePrint::TimeFormatter.new("%Y")
time = Time.local(2012, 01, 11, 1, 23, 45)
f.format(time).should == "2012" # default time format is set in config.rb
end
end
end
describe TablePrint::NoNewlineFormatter do
before(:each) do
@f = TablePrint::NoNewlineFormatter.new
end
describe "#format" do
it "replaces carriage returns with spaces" do
@f.format("foo\r\nbar").should == "foo bar"
end
it "replaces newlines with spaces" do
@f.format("foo\nbar").should == "foo bar"
end
end
end
describe TablePrint::FixedWidthFormatter do
before(:each) do
@f = TablePrint::FixedWidthFormatter.new(10)
end
describe "#format" do
it "pads a short field to the specified width" do
@f.format("asdf").should == "asdf "
end
it "truncates long fields with periods" do
@f.format("1234567890123456").should == "1234567..."
end
it "uses an empty string in place of nils" do
@f.format(nil).should == " "
end
it "turns objects into strings before trying to format them" do
@f.format(123).should == "123 "
end
end
end
table_print-1.5.7/spec/column_spec.rb 0000644 0000041 0000041 00000004447 13701574164 017666 0 ustar www-data www-data require 'spec_helper'
include TablePrint
describe Column do
let(:c) {Column.new(:data => ["Once upon a time", "there was a dark and stormy night"], :name => :tagline)}
it "remembers its name as a string" do
c.name.should == "tagline"
end
it "exposes the array of data representing the column" do
c.data.should == ["Once upon a time", "there was a dark and stormy night"]
end
describe "#add_formatter" do
it "stores the formatter" do
f = {}
c.add_formatter(f)
c.formatters.should == [f]
end
end
describe "#formatter=" do
it "adds the formatters individually" do
c.should_receive(:add_formatter).twice
c.formatters = [{}, {}]
end
end
describe "#display_method" do
it "returns the column's display method as a string" do
c = Column.new(:display_method => :boofar)
c.display_method.should == "boofar"
end
it "doesn't turn a lambda display method into a string" do
lam = lambda{}
c = Column.new(:display_method => lam)
c.display_method.should == lam
end
it "defaults to the column name" do
c = Column.new(:name => :boofar)
c.display_method.should == "boofar"
end
end
describe "#data_width" do
it "reflects the width of the data set" do
c.data_width.should == 33
end
it "includes the title in the calculation" do
c.name = "a horse is a horse of course of course"
c.data_width.should == 38
end
end
describe "#width" do
context "when default width is specified" do
it "uses the default width" do
c.default_width = 10
c.stub(:data_width => 15)
c.stub(:max_width => 20)
c.width.should == 10
end
it "isn't limited by the config width" do
c.default_width = 40
c.stub(:data_width => 50)
c.stub(:max_width => 20)
c.width.should == 40
end
end
context "When default width is not specified" do
it "uses the data width" do
c.default_width = nil
c.stub(:data_width => 10)
c.stub(:max_width => 20)
c.width.should == 10
end
it "is limited by the config width" do
c.default_width = nil
c.stub(:data_width => 30)
c.stub(:max_width => 20)
c.width.should == 20
end
end
end
end
table_print-1.5.7/spec/row_group_spec.rb 0000644 0000041 0000041 00000042446 13701574164 020415 0 ustar www-data www-data require 'spec_helper'
include TablePrint
describe RowRecursion do
let(:parent) { RowGroup.new }
let(:child) { Row.new }
describe "#set_column" do
it "assigns the column object to the column name" do
column = Column.new(:name => "foobar")
parent.set_column(column)
parent.column_for(:foobar).should == column
end
end
describe "#add_child" do
it "adds the child to my children" do
parent.add_child(child)
parent.child_count.should == 1
end
it "sets me as my child's parent" do
parent.add_child(child)
child.parent.should == parent
end
end
describe "#add_children" do
let (:child2) {Row.new}
it "adds all the children to myself" do
parent.add_children([child, child2])
parent.child_count.should == 2
end
it "sets me as their parent" do
parent.add_children([child, child2])
child.parent.should == parent
child2.parent.should == parent
end
end
describe "#columns" do
it "returns columns populated with names and data" do
child.set_cell_values(:title => 'foobar')
parent.add_child(child)
parent.columns.length.should == 1
parent.columns.first.name.should == 'title'
parent.columns.first.data.should == ['foobar']
end
it "gets the columns from the root node" do
parent.add_child(child)
child.set_cell_values(:title => 'foobar')
parent.columns.length.should == 1
child.columns.should == parent.columns
end
end
describe "#column_for" do
it "returns the column object for a given column name" do
parent.add_child(child)
child.set_cell_values(:title => 'foobar')
column = parent.columns.first
parent.column_for(:title).should == column
end
end
describe "#add_formatter" do
it "adds the formatter to the column object" do
parent.add_child(child)
child.set_cell_values(:title => 'foobar')
column = parent.columns.first
parent.add_formatter(:title, {})
column.formatters.should == [{}]
end
end
describe "#width" do
it "returns the total width of the columns" do
parent.add_child(r1 = Row.new)
parent.add_child(r2 = Row.new)
r1.set_cell_values(:title => 'foobar')
r2.set_cell_values(:subtitle => 'elemental')
parent.width.should == 18
end
end
describe "#horizontal_separator" do
it "returns hyphens equal to the table width" do
parent.add_child(r1 = Row.new)
parent.add_child(r2 = Row.new)
r1.set_cell_values(:title => 'a' * 5, :description => 'b' * 3, :category => 'c' * 10)
r2.set_cell_values(:title => 'a' * 6, :description => 'b' * 4, :category => 'c' * 9)
parent.header.size.should == parent.horizontal_separator.size
compare_rows(parent.horizontal_separator, '-' * 6 + '-|-' + '-' * 'description'.size + '-|-' + '-' * 10)
end
it "matches the header width" do
child.set_cell_values(:title => 'foobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobar')
child.horizontal_separator.should == '------------------------------' # 30 hyphens
end
end
describe "#header" do
it "returns the column names, padded to the proper width, separated by the | character" do
child.set_cell_values(:title => 'first post', :author => 'chris', :subtitle => 'first is the worst')
compare_rows(child.header, "AUTHOR | SUBTITLE | TITLE ")
end
end
end
describe TablePrint::RowGroup do
describe "#raw_column_data" do
it "returns the column data from its child rows" do
group = RowGroup.new
group.add_child(Row.new.set_cell_values(:title => 'foo'))
group.add_child(Row.new.set_cell_values(:title => 'bar'))
group.raw_column_data(:title).should == ['foo', 'bar']
end
end
end
def compare_rows(actual_rows, expected_rows)
actual_rows.split("\n").length.should == expected_rows.split("\n").length
actual_rows.split("\n").zip(expected_rows.split("\n")).each do |actual, expected|
actual.split(//).sort.join.should == expected.split(//).sort.join
end
end
describe TablePrint::Row do
let(:row) { Row.new.set_cell_values({'title' => "wonky", 'author' => "bob jones", 'pub_date' => "2012"}) }
describe "#format" do
it "formats the row with padding" do
compare_rows(row.format, "wonky | bob jones | 2012 ")
end
it "also formats the children" do
row.add_child(RowGroup.new.add_child(Row.new.set_cell_values(:title => "wonky2", :author => "bob jones2", :pub_date => "20122")))
compare_rows(row.format, "wonky | bob jones | 2012 \nwonky2 | bob jones2 | 20122 ")
end
end
describe "#apply_formatters" do
it "calls the format method on each formatter for that column" do
Sandbox.add_class("DoubleFormatter")
Sandbox.add_method("DoubleFormatter", :format) { |value| value * 2 }
Sandbox.add_class("ChopFormatter")
Sandbox.add_method("ChopFormatter", :format) { |value| value[0..-2] }
f1 = Sandbox::DoubleFormatter.new
f2 = Sandbox::ChopFormatter.new
row.stub(:column_for) {OpenStruct.new(:width => 11, :formatters => [f1, f2])}
row.apply_formatters(:title, "foobar").should == "foobarfooba"
end
it "uses the config'd time_format to format times" do
row.stub(:column_for) {OpenStruct.new(:width => 20, :formatters => [], :time_format => "%Y %m %d")}
time_formatter = TablePrint::TimeFormatter.new
TablePrint::TimeFormatter.should_receive(:new).with("%Y %m %d") {time_formatter}
row.apply_formatters(:title, Time.local(2012, 6, 1, 14, 20, 20))
end
end
describe "#raw_column_data" do
it "returns all the values for a given column" do
row = Row.new.set_cell_values(:title => 'one', :author => 'two')
group = RowGroup.new
['two', 'three', 'four', 'five', 'six', 'seven'].each do |title|
group.add_child(Row.new.set_cell_values(:title => title))
end
row.add_child(group)
row.raw_column_data('title').should == ['one', 'two', 'three', 'four', 'five', 'six', 'seven']
end
end
describe "#collapse" do
# row: foo
# group
# row: bar
# => foo | bar
context "for a single row in a single child group" do
before(:each) do
@row = Row.new
@row.set_cell_values(:foo => "foo").add_child(
RowGroup.new.add_child(
Row.new.set_cell_values(:bar => "bar")
)
)
@row.collapse!
end
it "pulls the cells up into the parent" do
@row.cells.should == {"foo" => "foo", "bar" => "bar"}
end
it "dereferences the now-defunct group" do
@row.children.length.should == 0
end
end
# row: foo
# group
# row: bar
# row: baz
# => foo | bar
# | baz
context "for two rows in a single child group" do
before(:each) do
@row = Row.new
@row.set_cell_values(:foo => "foo").add_child(
RowGroup.new.add_children([
Row.new.set_cell_values(:bar => "bar"),
Row.new.set_cell_values(:bar => "baz")
])
)
@row.collapse!
end
it "pulls the cells from the first row up into the parent" do
@row.cells.should == {"foo" => "foo", "bar" => "bar"}
end
it "deletes the absorbed row but leaves the second row in the group" do
@row.children.length.should == 1
@row.children.first.children.length.should == 1
@row.children.first.children.first.cells.should == {"bar" => "baz"}
end
end
# row: foo
# group
# row: bar
# group
# row: baz
# => foo | bar | baz
context "for two groups with a single row each" do
before(:each) do
@row = Row.new
@row.set_cell_values(:foo => "foo").add_children([
RowGroup.new.add_child(
Row.new.set_cell_values(:bar => "bar")
),
RowGroup.new.add_child(
Row.new.set_cell_values(:baz => "baz")
)
])
@row.collapse!
end
it "pulls the cells from both groups into the parent" do
@row.cells.should == {"foo" => "foo", "bar" => "bar", "baz" => "baz"}
end
it "dereferences both now-defunct groups" do
@row.children.length.should == 0
end
end
# row: foo
# group
# row: bar
# group
# => foo | bar |
context "for two groups, one of which has no rows (aka, we hit an empty association)" do
before(:each) do
@row = Row.new
@row.set_cell_values(:foo => "foo").add_children([
RowGroup.new.add_child(
Row.new.set_cell_values(:bar => "bar")
),
RowGroup.new
])
@row.collapse!
end
it "pulls the cells from both groups into the parent" do
@row.cells.should == {"foo" => "foo", "bar" => "bar"}
end
it "dereferences both now-defunct groups" do
@row.children.length.should == 0
end
end
# row: foo
# group
# row: bar
# group
# row: baz
# row: bazaar
# => foo | bar | baz
# | bazaar
context "for two groups, one with a single row and one with two rows" do
before(:each) do
@row = Row.new
@row.set_cell_values(:foo => "foo").add_children([
RowGroup.new.add_child(
Row.new.set_cell_values(:bar => "bar")
),
RowGroup.new.add_children([
Row.new.set_cell_values(:baz => "baz"),
Row.new.set_cell_values(:baz => "bazaar"),
]),
])
@row.collapse!
end
it "pulls the single row and the first row from the double into itself" do
@row.cells.should == {"foo" => "foo", "bar" => "bar", "baz" => "baz"}
end
it "keeps the second row from the second group in its own group" do
@row.children.length.should == 1
@row.children.first.children.length.should == 1
@row.children.first.children.first.cells.should == {"baz" => "bazaar"}
end
end
# row: foo
# group
# row: bar
# group
# row: baz
# => foo | bar | baz
context "for two nested groups, each with one row" do
before(:each) do
@row = Row.new
@row.set_cell_values(:foo => "foo").add_child(
RowGroup.new.add_child(
Row.new.set_cell_values(:bar => "bar").add_child(
RowGroup.new.add_child(
Row.new.set_cell_values(:baz => "baz")
)
)
)
)
@row.collapse!
end
it "pulls the cells from both groups into the parent" do
@row.cells.should == {"foo" => "foo", "bar" => "bar", "baz" => "baz"}
end
it "dereferences both now-defunct groups" do
@row.children.length.should == 0
end
end
# row: foo
# group
# row: bar
# group
# row: baz
# row: bazaar
# => foo | bar | baz
# | bazaar
context "for a child with one row, which itself has multiple rows" do
before(:each) do
@row = Row.new
@row.set_cell_values(:foo => "foo").add_child(
RowGroup.new.add_child(
Row.new.set_cell_values(:bar => "bar").add_child(
RowGroup.new.add_children([
Row.new.set_cell_values(:baz => "baz"),
Row.new.set_cell_values(:baz => "bazaar")
])
)
)
)
@row.collapse!
end
it "pulls the first row from each group up into itself" do
@row.cells.should == {"foo" => "foo", "bar" => "bar", "baz" => "baz"}
end
it "deletes only the intermediary group" do
@row.children.length.should == 1
@row.children.first.children.length.should == 1
@row.children.first.children.first.cells.should == {"baz" => "bazaar"}
end
end
# row: foo
# group
# row: bar
# row: bar2
# group
# row: bazaar
# row: bazaar2
# => foo | bar |
# | bar2 |
# | | bazaar
# | | bazaar2
context "for multiple children with multiple rows" do
before(:each) do
@row = Row.new
@row.set_cell_values(:foo => "foo").add_children([
RowGroup.new.add_children([
Row.new.set_cell_values(:bar => "bar"),
Row.new.set_cell_values(:bar => "bar2"),
]),
RowGroup.new.add_children([
Row.new.set_cell_values(:baz => "bazaar"),
Row.new.set_cell_values(:baz => "bazaar2"),
])
])
@row.collapse!
end
it "pulls the first row from the first group into the parent" do
@row.cells.should == {"foo" => "foo", "bar" => "bar"}
end
it "leaves the second row in the first group" do
@row.children.length.should == 2
@row.children.first.children.length.should == 1
@row.children.first.children.first.cells.should == {"bar" => "bar2"}
end
it "leaves the second group alone" do
@row.children.last.children.length.should == 2
@row.children.last.children.first.cells.should == {"baz" => "bazaar"}
@row.children.last.children.last.cells.should == {"baz" => "bazaar2"}
end
end
# row: foo
# group
# row: bar
# group
# row: bare
# row: bart
# row: baz
# group
# row: bazaar
# row: bizarre
# => foo | bar | bare
# | | bart
# | baz | bazaar
# | | bizarre
context "for multiple children with multiple children" do
before(:each) do
@row = Row.new
@row.set_cell_values(:foo => "foo").add_child(
RowGroup.new.add_children([
Row.new.set_cell_values(:bar => "bar").add_child(
RowGroup.new.add_children([
Row.new.set_cell_values(:barry => "bare"),
Row.new.set_cell_values(:barry => "bart")
])
),
Row.new.set_cell_values(:bar => "baz").add_child(
RowGroup.new.add_children([
Row.new.set_cell_values(:barry => "bazaar"),
Row.new.set_cell_values(:barry => "bizarre")
])
)
])
)
@row.collapse!
end
it "pulls the first row from the first child into itself" do
@row.cells.should == {"foo" => "foo", "bar" => "bar", "barry" => "bare"}
end
it "leaves the second row from the first child in the first group" do
@row.children.first.children.first.cells.should == {"barry" => "bart"}
end
it "collapses the second group" do
@row.children.last.children.first.cells.should == {"bar" => "baz", "barry" => "bazaar"}
@row.children.last.children.first.children.first.children.first.cells.should == {"barry" => "bizarre"}
end
end
end
end
table_print-1.5.7/spec/spec_helper.rb 0000644 0000041 0000041 00000000251 13701574164 017635 0 ustar www-data www-data require 'cat'
gem 'rspec'
require 'table_print'
require 'ostruct'
RSpec.configure do |c|
c.filter_run :focus => true
c.run_all_when_everything_filtered = true
end
table_print-1.5.7/.gitignore 0000644 0000041 0000041 00000001407 13701574164 016061 0 ustar www-data www-data Gemfile.lock
# rcov generated
coverage
# rdoc generated
rdoc
# yard generated
doc
.yardoc
# bundler
.bundle
# jeweler generated
pkg
# Have editor/IDE/OS specific files you need to ignore? Consider using a global gitignore:
#
# * Create a file at ~/.gitignore
# * Include files you want ignored
# * Run: git config --global core.excludesfile ~/.gitignore
#
# After doing this, these files will be ignored in all your git projects,
# saving you from having to 'pollute' every project you touch with them
#
# Not sure what to needs to be ignored for particular editors/OSes? Here's some ideas to get you started. (Remember, remove the leading # of the line)
#
# For MacOS:
#
#.DS_Store
#
# For TextMate
#*.tmproj
#tmtags
#
# For emacs:
#*~
#\#*
#.\#*
#
# For vim:
#*.swp
table_print-1.5.7/.document 0000644 0000041 0000041 00000000067 13701574164 015711 0 ustar www-data www-data lib/**/*.rb
bin/*
-
features/**/*.feature
LICENSE.txt
table_print-1.5.7/table_print.gemspec 0000644 0000041 0000041 00000002205 13701574164 017736 0 ustar www-data www-data # -*- encoding: utf-8 -*-
require File.expand_path('../lib/table_print/version', __FILE__)
Gem::Specification.new do |gem|
gem.name = "table_print"
gem.authors = ["Chris Doyle"]
gem.email = ["chris@arches.io"]
gem.email = "chris@arches.io"
gem.description = "TablePrint turns objects into nicely formatted columns for easy reading. Works great in rails console, works on pure ruby objects, autodetects columns, lets you traverse ActiveRecord associations. Simple, powerful."
gem.summary = "Turn objects into nicely formatted columns for easy reading"
gem.homepage = "http://tableprintgem.com"
gem.version = TablePrint::VERSION
gem.license = 'MIT'
gem.files = `git ls-files`.split($\)
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
gem.require_paths = ["lib"]
gem.add_development_dependency 'cat', '~> 0.2.1'
gem.add_development_dependency 'cucumber', '~> 2.4.0'
gem.add_development_dependency 'rspec', '~> 2.11.0'
gem.add_development_dependency 'rake', '~> 0.9.2'
end
table_print-1.5.7/Rakefile 0000644 0000041 0000041 00000002223 13701574164 015533 0 ustar www-data www-data require 'rubygems'
require 'bundler'
begin
Bundler.setup(:default, :development)
rescue Bundler::BundlerError => e
$stderr.puts e.message
$stderr.puts "Run `bundle install` to install missing gems"
exit e.status_code
end
require 'rake'
require 'rspec/core/rake_task'
desc 'Default: run specs and cucumber features.'
if RUBY_VERSION < '1.9'
task :default => [:spec, :cucumber_187]
else
task :default => [:spec, :cucumber]
end
desc "Run specs"
RSpec::Core::RakeTask.new do |t|
end
require "cucumber/rake/task"
desc 'Run cucumber features'
Cucumber::Rake::Task.new(:cucumber) do |task|
task.cucumber_opts = ["features"]
end
desc 'Run cucumber features for ruby 1.8.7'
Cucumber::Rake::Task.new(:cucumber_187) do |task|
task.cucumber_opts = ["-t", "~@ruby19", "features"]
end
begin
require 'rdoc/task'
rescue LoadError
require 'rake/rdoctask' # deprecated in Ruby 1.9.3 but needed for Ruby 1.8.7 and JRuby
end
Rake::RDocTask.new do |rdoc|
version = File.exist?('VERSION') ? File.read('VERSION') : ""
rdoc.rdoc_dir = 'rdoc'
rdoc.title = "table_print #{version}"
rdoc.rdoc_files.include('README*')
rdoc.rdoc_files.include('lib/**/*.rb')
end
table_print-1.5.7/lib/ 0000755 0000041 0000041 00000000000 13701574164 014635 5 ustar www-data www-data table_print-1.5.7/lib/table_print/ 0000755 0000041 0000041 00000000000 13701574164 017140 5 ustar www-data www-data table_print-1.5.7/lib/table_print/formatter.rb 0000644 0000041 0000041 00000001754 13701574164 021477 0 ustar www-data www-data module TablePrint
class TimeFormatter
def initialize(time_format=nil)
@format = time_format
@format ||= TablePrint::Config.time_format
end
def format(value)
return value unless value.is_a? Time
value.strftime @format
end
end
class NoNewlineFormatter
def format(value)
value.to_s.gsub(/\r\n/, "\n").gsub(/\n/, " ")
end
end
class FixedWidthFormatter
attr_accessor :width
def initialize(width)
self.width = width
end
def format(value)
padding = width - length(value.to_s)
truncate(value) + (padding < 0 ? '' : " " * padding)
end
private
def truncate(value)
return "" unless value
value = value.to_s
return value unless value.length > width
"#{value[0..width-4]}..."
end
def length(str)
if TablePrint::Config.multibyte
str.each_char.collect{|c| c.bytesize == 1 ? 1 : 2}.inject(0, &:+)
else
str.length
end
end
end
end
table_print-1.5.7/lib/table_print/version.rb 0000644 0000041 0000041 00000000053 13701574164 021150 0 ustar www-data www-data module TablePrint
VERSION = "1.5.7"
end
table_print-1.5.7/lib/table_print/column.rb 0000644 0000041 0000041 00000002665 13701574164 020773 0 ustar www-data www-data module TablePrint
class Column
attr_reader :formatters
attr_accessor :name, :data, :time_format, :default_width, :min_width, :fixed_width
def initialize(attr_hash={})
@formatters = []
attr_hash.each do |k, v|
self.send("#{k}=", v)
end
end
def name=(n)
@name = n.to_s
end
def formatters=(formatter_list)
formatter_list.each do |f|
add_formatter(f)
end
end
def display_method=(method)
method = method.to_s unless method.is_a? Proc
@display_method = method
end
def display_method
@display_method ||= name
end
def add_formatter(formatter)
@formatters << formatter
end
def data_width
if multibyte_count
[
name.each_char.collect{|c| c.bytesize == 1 ? 1 : 2}.inject(0, &:+),
Array(data).compact.collect(&:to_s).collect{|m| m.each_char.collect{|n| n.bytesize == 1 ? 1 : 2}.inject(0, &:+)}.max
].compact.max || 0
else
[
name.length,
Array(data).compact.collect(&:to_s).collect(&:length).max
].compact.max || 0
end
end
def width
return fixed_width if fixed_width
width = [(default_width || max_width), data_width].min
[(min_width || 0), width].max
end
private
def max_width
TablePrint::Config.max_width
end
def multibyte_count
TablePrint::Config.multibyte
end
end
end
table_print-1.5.7/lib/table_print/returnable.rb 0000644 0000041 0000041 00000001004 13701574164 021623 0 ustar www-data www-data module TablePrint
class Returnable
def initialize(string_value="")
@string_value = string_value
end
def set(klass, *config)
TablePrint::Config.set(klass, config)
"Set table_print config for #{klass}"
end
def clear(klass)
TablePrint::Config.clear(klass)
"Cleared table_print config for #{klass}"
end
def config_for(klass)
TablePrint::Config.for(klass)
end
def to_s
@string_value
end
def inspect
to_s
end
end
end
table_print-1.5.7/lib/table_print/fingerprinter.rb 0000644 0000041 0000041 00000005536 13701574164 022354 0 ustar www-data www-data module TablePrint
class Fingerprinter
def lift(columns, object)
@column_names_by_display_method = {}
columns.each { |c| @column_names_by_display_method[c.display_method] = c.name }
column_hash = display_methods_to_nested_hash(columns.collect(&:display_method))
hash_to_rows("", column_hash, object)
end
def hash_to_rows(prefix, hash, objects)
rows = []
# convert each object into its own row
Array(objects).each do |target|
row = populate_row(prefix, hash, target)
rows << row
# make a group and recurse for the columns we don't handle
groups = create_child_group(prefix, hash, target)
row.add_children(groups) unless groups.all? {|g| g.children.empty?}
end
rows
end
def populate_row(prefix, hash, target)
row = TablePrint::Row.new()
# populate a row with the columns we handle
cells = {}
handleable_columns(hash).each do |method|
display_method = (prefix == "" ? method : "#{prefix}.#{method}")
if method.is_a? Proc
cell_value = method.call(target)
elsif target.is_a? Hash and target.keys.include? method.to_sym
cell_value = target[method.to_sym]
elsif target.is_a? Hash and target.keys.include? method
cell_value = target[method]
elsif target.respond_to? method
cell_value ||= target.send(method)
else
cell_value = "Method Missing"
end
cells[@column_names_by_display_method[display_method]] = cell_value
end
row.set_cell_values(cells)
end
def create_child_group(prefix, hash, target)
passable_columns(hash).collect do |name|
recursing_prefix = "#{prefix}#{'.' unless prefix == ''}#{name}"
group = RowGroup.new
group.add_children hash_to_rows(recursing_prefix, hash[name], target.send(name))
group
end
end
def handleable_columns(hash)
# get the keys where the value is an empty hash
hash.select { |k, v| v == {} }.collect { |k, v| k }
end
def passable_columns(hash)
# get the keys where the value is not an empty hash
hash.select { |k, v| v != {} }.collect { |k, v| k }
end
def display_methods_to_nested_hash(display_methods)
extended_hash = {}.extend TablePrint::HashExtensions::ConstructiveMerge
# turn each column chain into a nested hash and add it to the output
display_methods.inject(extended_hash) do |hash, display_method|
hash.constructive_merge!(display_method_to_nested_hash(display_method))
end
end
def display_method_to_nested_hash(display_method)
hash = {}
return {display_method => {}} if display_method.is_a? Proc
display_method.split(".").inject(hash) do |hash_level, method|
hash_level[method] ||= {}
end
hash
end
end
end
table_print-1.5.7/lib/table_print/hash_extensions.rb 0000644 0000041 0000041 00000001476 13701574164 022677 0 ustar www-data www-data module TablePrint
module HashExtensions
module ConstructiveMerge
def constructive_merge(hash)
target = dup
hash.keys.each do |key|
if hash[key].is_a? Hash and self[key].is_a? Hash
target[key].extend ConstructiveMerge
target[key] = target[key].constructive_merge(hash[key])
next
end
target[key] = hash[key]
end
target
end
def constructive_merge!(hash)
target = self
hash.keys.each do |key|
if hash[key].is_a? Hash and self[key].is_a? Hash
target[key].extend ConstructiveMerge
target[key] = target[key].constructive_merge(hash[key])
next
end
target[key] = hash[key]
end
target
end
end
end
end
table_print-1.5.7/lib/table_print/row_group.rb 0000644 0000041 0000041 00000013616 13701574164 021517 0 ustar www-data www-data module TablePrint
module RowRecursion
attr_accessor :parent
attr_accessor :children
def initialize
@children = []
@columns = {}
end
def add_child(child)
@children << child
child.parent = self
end
def insert_children(i, children)
@children.insert(i, children).flatten!
children.each {|c| c.parent = self }
self
end
def add_children(children)
@children.concat children
children.each { |c| c.parent = self }
self
end
def child_count
@children.length
end
def set_column(column)
return parent.set_column(column) if parent
@columns[column.name.to_s] = column
end
def columns
return parent.columns if parent
raw_column_names.collect{|k, v| column_for(k)}
end
def column_count
return parent.column_count if parent
@columns.size
end
def column_for(name)
return parent.column_for(name) if parent
column = @columns[name.to_s] ||= Column.new(:name => name)
# assign the data sets to the column before we return it
# do this as late as possible, since new rows could be added at any time
column.data ||= raw_column_data(column.name)
column
end
def width
return parent.width if parent
columns.collect(&:width).inject(&:+) + (columns.length - 1) * 3 # add (n-1)*3 for the 3-character separator
end
def horizontal_separator
columns.collect do |column|
'-' * column.width
end.join("-#{TablePrint::Config.separator}-")
end
def header
padded_names = columns.collect do |column|
f = FixedWidthFormatter.new(column.width)
f.format(column.name)
end
header_string = padded_names.join(" #{TablePrint::Config.separator} ")
header_string.upcase! if TablePrint::Config.capitalize_headers
header_string
end
def add_formatter(name, formatter)
return unless column_for(name)
column_for(name).add_formatter(formatter)
end
end
class RowGroup
include RowRecursion
def initialize
super
@skip_first_row = false
end
def raw_column_data(column_name)
@children.collect { |r| r.raw_column_data(column_name) }.flatten
end
def raw_column_names
return @raw_column_names if @raw_column_names
@raw_column_names = @children.collect { |r| r.raw_column_names }.flatten.uniq
end
# this is a development tool, to show the structure of the row/row_group tree
def vis(prefix="")
puts "#{prefix}group"
children.each{|c| c.vis(prefix + " ")}
end
def collapse!
@children.each(&:collapse!)
end
# TODO: rename this to_s
def format
rows = @children
rows = @children[1..-1] if @skip_first_row
rows ||= []
rows = rows.collect { |row| row.format }.join("\n")
return nil if rows.length == 0
rows
end
def skip_first_row!
@skip_first_row = true
end
end
class Row
attr_reader :cells
include RowRecursion
def initialize
super
@cells = {}
end
# this is a development tool, to show the structure of the row/row_group tree
def vis(prefix="")
puts "#{prefix}row #{cells.inspect.to_s}"
children.each{|c| c.vis(prefix + " ")}
end
def collapse!
children.each(&:collapse!) # depth-first. start collapsing from the bottom and work our way up.
to_absorb = []
children.each do |group|
next unless can_absorb?(group)
to_absorb << group
end
to_absorb.each do |absorbable_group|
absorbable_row = absorbable_group.children.shift
# missing associations create groups with no rows
children.delete(absorbable_group) and next unless absorbable_row
@cells.merge!(absorbable_row.cells)
i = children.index(absorbable_group)
children.delete(absorbable_group) if absorbable_group.children.empty?
insert_children(i, absorbable_row.children) if absorbable_row.children.any?
end
end
def set_cell_values(values_hash)
values_hash.each do |k, v|
@cells[k.to_s] = v
end
self
end
def format
column_names = columns.collect(&:name)
output = [column_names.collect { |name| apply_formatters(name, @cells[name]) }.join(" #{TablePrint::Config.separator} ")]
output.concat @children.collect { |g| g.format }
output.join("\n")
end
def absorb_children(column_names, rollup)
@children.each do |group|
next unless can_absorb?(group)
group.skip_first_row!
column_names.collect do |name|
next unless group.children and group.children.length > 0
value = group.children.first.cells[name]
rollup[name] = value if value
end
end
end
def raw_column_data(column_name)
output = [@cells[column_name.to_s]]
output << @children.collect { |g| g.raw_column_data(column_name) }
output.flatten
end
def raw_column_names
output = [@cells.keys]
output << @children.collect { |g| g.raw_column_names }
output.flatten.uniq
end
def apply_formatters(column_name, value)
column_name = column_name.to_s
return value unless column_for(column_name)
column = column_for(column_name)
formatters = []
formatters.concat(Array(column.formatters))
formatters << TimeFormatter.new(column.time_format)
formatters << NoNewlineFormatter.new
formatters << FixedWidthFormatter.new(column_for(column_name).width)
# successively apply the formatters for a column
formatters.inject(value) do |inner_value, formatter|
formatter.format(inner_value)
end
end
def can_absorb?(group)
return true if group.child_count == 1
return false if @already_absorbed_a_multigroup
@already_absorbed_a_multigroup = true # only call this method once
end
end
end
table_print-1.5.7/lib/table_print/config_resolver.rb 0000644 0000041 0000041 00000005221 13701574164 022653 0 ustar www-data www-data module TablePrint
class ConfigResolver
def initialize(klass, default_column_names, *options)
@column_hash = {}
@default_columns = default_column_names.collect { |name| option_to_column(name) }
@included_columns = []
@excepted_columns = []
@only_columns = []
process_option_set(TablePrint::Config.for(klass))
process_option_set(options)
end
def process_option_set(options)
options = [options].flatten
options.delete_if { |o| o == {} }
# process special symbols
@included_columns.concat [get_and_remove(options, :include)].flatten
@included_columns.map! do |option|
if option.is_a? Column
option
else
option_to_column(option)
end
end
@included_columns.each do |c|
@column_hash[c.name] = c
end
# excepted columns don't need column objects since we're just going to throw them out anyway
@excepted_columns.concat [get_and_remove(options, :except)].flatten
# anything that isn't recognized as a special option is assumed to be a column name
options.compact!
@only_columns = options.collect { |name| option_to_column(name) } unless options.empty?
end
def get_and_remove(options_array, key)
except = options_array.select do |option|
option.is_a? Hash and option.keys.include? key
end
return [] if except.empty?
except = except.first
option_of_interest = except.fetch(key)
except.delete(key)
options_array.delete(except) if except.keys.empty? # if we've taken all the info from this option, get rid of it
option_of_interest
end
def option_to_column(option)
if option.is_a? Hash
name = option.keys.first
if option[name].is_a? Proc
option = {:name => name, :display_method => option[name]}
else
option = option[name].merge(:name => name)
end
else
option = {:name => option}
end
if option.has_key? :width
option[:default_width] = option.delete(:width)
end
if option.has_key? :display_name
option[:display_method] = option[:name]
option[:name] = option.delete(:display_name)
end
c = Column.new(option)
@column_hash[c.name] = c
c
end
def usable_column_names
base = @default_columns
base = @only_columns unless @only_columns.empty?
Array(base).collect(&:name) + Array(@included_columns).collect(&:name) - Array(@excepted_columns).collect(&:to_s)
end
def columns
usable_column_names.collect do |name|
@column_hash[name]
end
end
end
end
table_print-1.5.7/lib/table_print/printable.rb 0000644 0000041 0000041 00000002021 13701574164 021440 0 ustar www-data www-data module TablePrint
module Printable
# Sniff the data class for non-standard methods to use as a baseline for display
def self.default_display_methods(target)
if target.class.respond_to? :columns
if target.class.columns.first.respond_to? :name
# eg ActiveRecord
return target.class.columns.collect(&:name)
else
# eg Sequel
return target.class.columns
end
end
# eg mongoid
return target.fields.keys if target.respond_to? :fields and target.fields.is_a? Hash
return target.keys if target.is_a? Hash
return target.members.collect(&:to_sym) if target.is_a? Struct
methods = []
target.methods.each do |method_name|
method = target.method(method_name)
if method.owner == target.class
if method.arity == 0 #
methods << method_name.to_s
end
end
end
methods.delete_if { |m| m[-1].chr == "!" } # don't use dangerous methods
methods
end
end
end
table_print-1.5.7/lib/table_print/config.rb 0000644 0000041 0000041 00000003341 13701574164 020733 0 ustar www-data www-data module TablePrint
class Config
DEFAULT_MAX_WIDTH = 30
DEFAULT_TIME_FORMAT = "%Y-%m-%d %H:%M:%S"
DEFAULT_IO = $stdout
DEFAULT_CAPITALIZE_HEADERS = true
DEFAULT_SEPARATOR = "|"
@@max_width = DEFAULT_MAX_WIDTH
@@time_format = DEFAULT_TIME_FORMAT
@@multibyte = false
@@io = DEFAULT_IO
@@capitalize_headers = true
@@separator = DEFAULT_SEPARATOR
@@klasses = {}
def self.set(klass, val)
if klass.is_a? Class
@@klasses[klass] = val # val is a hash of column options
else
TablePrint::Config.send("#{klass}=", val.first)
end
end
def self.for(klass)
@@klasses.fetch(klass) {}
end
def self.clear(klass)
if klass.is_a? Class
@@klasses.delete(klass)
else
original_value = TablePrint::Config.const_get("DEFAULT_#{klass.to_s.upcase}")
TablePrint::Config.send("#{klass}=", original_value)
end
end
def self.max_width
@@max_width
end
def self.max_width=(width)
@@max_width = width
end
def self.multibyte
@@multibyte
end
def self.multibyte=(width)
@@multibyte = width
end
def self.time_format
@@time_format
end
def self.time_format=(format)
@@time_format = format
end
def self.capitalize_headers
@@capitalize_headers
end
def self.capitalize_headers=(caps)
@@capitalize_headers = caps
end
def self.separator
@@separator
end
def self.separator=(separator)
@@separator = separator
end
def self.io
@@io
end
def self.io=(io)
raise StandardError.new("IO object must respond to :puts") unless io.respond_to? :puts
@@io = io
end
end
end
table_print-1.5.7/lib/table_print.rb 0000644 0000041 0000041 00000004422 13701574164 017467 0 ustar www-data www-data require 'table_print/column'
require 'table_print/config_resolver'
require 'table_print/config'
require 'table_print/fingerprinter'
require 'table_print/formatter'
require 'table_print/hash_extensions'
require 'table_print/printable'
require 'table_print/row_group'
require 'table_print/returnable'
module TablePrint
class Printer
def self.table_print(data, options={})
p = new(data, options)
p.table_print
end
def initialize(data, options={})
@data = Array(data).compact
@options = options
@columns = nil
@start_time = Time.now
end
def table_print
return "No data." if @data.empty?
# it's groups all the way down
# make a top-level group to hold everything we're about to do
group = TablePrint::RowGroup.new
# parse the config and attach it to the group
columns.each do |c|
group.set_column(c)
end
# copy data from original objects into the group
group_data = (@data.first.is_a?(Hash) || @data.first.is_a?(Struct)) ? [@data] : @data
group_data.each do |data|
group.add_children(Fingerprinter.new.lift(columns, data))
end
# munge the tree of data we created, to condense the output
group.collapse!
return "No data." if group.columns.empty?
# turn everything into a string for output
[group.header, group.horizontal_separator, group.format].join("\n")
end
def message
return "Printed with config" if configged?
(Time.now - @start_time).to_s
# the message is used to initiate Returnable
# whose argument is regarded in ruby 2.7.1 as string
# (ruby 2.7.1 calls ".includes?" method on this argument)
end
private
def configged?
!!Config.for(@data.first.class)
end
def columns
return @columns if @columns
defaults = TablePrint::Printable.default_display_methods(@data.first)
c = TablePrint::ConfigResolver.new(@data.first.class, defaults, @options)
@columns = c.columns
end
end
end
def tp(data=Class, *options)
printer = TablePrint::Printer.new(data, options)
TablePrint::Config.io.puts printer.table_print unless data.is_a? Class
TablePrint::Returnable.new(printer.message) # we have to return *something*, might as well be execution time.
end
table_print-1.5.7/README.rdoc 0000644 0000041 0000041 00000020333 13701574164 015676 0 ustar www-data www-data = table_print
{}[http://travis-ci.org/arches/table_print]
{
}[https://codeclimate.com/github/arches/table_print]
TablePrint shows objects in nicely formatted columns for easy reading. It even lets you nest other tables
of related objects, contextualizing data across tables. It's incredibly flexible, yet simple, making it easy to
see exactly the data you care about.
This {three minute screencast}[http://tableprintgem.com] will give you a quick overview of table_print's most
powerful features and how to use them.
http://arches.io/tp_example.gif
== Installation
# Install as a standalone gem
$ gem install table_print
# Or install within rails
In your Gemfile: gem "table_print"
$ bundle install
== Usage
# Outside rails
$ irb
> require 'table_print'
> tp array_of_objects, options
# Inside rails, the gem has already been required by your Gemfile so all you need to do is
$ rails c
> tp array_of_objects, options
You should see something like this:
> tp Book.all
AUTHOR | SUMMARY | TITLE
------------------|---------------------------------|------------------
Michael Connelly | Another book by Michael Con... | The Fifth Witness
Manning Marable | From acclaimed historian Ma... | Malcolm X
Tina Fey | Worth it. -Trees | Bossypants
TablePrint tries to use sensible defaults to choose the columns to show. If you're inspecting ActiveRecord objects, it
uses the ActiveRecord column names. You can customize the output to show fewer columns, or show other methods you've written
on your model. Use symbols or strings to reference the columns.
# Maybe you store a user's hourly rate but you want to see their yearly income
tp User.limit(30), :include => :yearly_income, :except => :hourly_rate
# Maybe all you care about is their mailing info
tp User.limit(30), :address, 'city', 'state', :zip
If you're not using ActiveRecord, the TablePrint default is to show all the methods defined directly on your object (nothing
from superclasses/mixins).
You can reference nested objects with the method chain required to reach them. Say you had some users who wrote books, and those
books had photos.
> tp Author.limit(3), "name", "books.title", "books.photos.caption"
NAME | BOOKS.TITLE | BOOKS.PHOTOS.CAPTION
------------------|-------------------|----------------------------------
Michael Connelly | The Fifth Witness | Susan was running, fast, away...
| | Along came a spider.
| Malcolm X |
| Bossypants | Yes! Yes! A thousand times ye...
| | Don't see many like you aroun...
Carrot Top | |
Milton Greene | How I Learned | Once upon a time, I was a sma...
| | Lemons are yellow, limes are ...
| | Never as a woman her age. I l...
| Desperados | Avast.
| | Giraffes lived a peaceful exi...
=== Column options
Pass options to individual columns through the options hash by using the display method as the hash key. Eg, if you wanted
a skinny email column, set the width explicitly:
tp User.all, :email => {:width => 12}
tp User.all, :id, {:email => {:width => 12}}, :age
Available column options:
* *display_method* - string/symbol/proc - used to populate the column. Can be a method name or a proc. See below.
* *formatters* - array of objects - each will be called in turn as the cells are printed. Must define a 'format' method. See below.
* *time_format* - string - passed to strftime[http://www.ruby-doc.org/core-1.9.3/Time.html#method-i-strftime], only affects time columns
* *width* - integer - how wide you want your column.
* *display_name* - string - useful if you want spaces in your column name
* *separator* - string - default is vertical bar for console and markdown, change it to a comma for CSV output
==== Display method
Columns are named after hash keys. To rename a column, use the name you want as the key, and pass the method as an option.
tp User.all, :active => {:display_method => :active_in_the_past_six_months} # the User class defines active_in_the_past_six_months method
==== Lambdas
You can pass a proc as the display_method for a column:
tp User.all, :email, :monthly_payment, yearly_payment: lambda{|u| u.monthly_payment * 12}
Or if you want to pass other options along with the lambda:
tp User.all, :yearly_payment => {:display_method => lambda{|u| u.monthly_payment * 12}, :width => 10}
Make sure to add curly braces if you have more than one column with a lambda or if this column isn't the last one.
tp User.all, :email, { daily_payment: lambda { |u| u.monthly_payment / 30} }, :monthly_payment, { yearly_payment: lambda { |u| u.monthly_payment * 12} }
==== Column formatters
Similar to a lambda column, you can use a column formatter to reuse code across prints. Any object with a 'format' method
can be used to filter a column. This could also be used for coloring output.
class NoNewlineFormatter
def format(value)
value.to_s.gsub(/\r\n/, " ")
end
end
tp User.all, :bio => {:formatters => [NoNewlineFormatter.new]} # strip newlines from user bios
=== HTML Output
Currently the best way to show table_print output on an HTML page is using a
tag. You can create a helper
method to send table_print output directly into your tag:
def tp_pre data, options={}
content_tag :pre, TablePrint::Printer.new(data, options).table_print
end
=== CSV Output
Set the column separator to get an output you can save as a CSV:
tp.set :separator, ","
Since your CSV viewer probably already handles column widths for you, setting +max_width+ to something very large will help you export your full data set. Otherwise, this CSV output will still be truncated to the default max_width of 30 characters.
=== Config
Use tp.set and tp.clear to set options on a class-by-class basis.
tp.set User, :id, :email # now whenever you print users, the only columns shown will be id and email
> tp User.first
ID | EMAIL
---|------------
1 | foo@bar.com
# the config sets a 'baseline' for each print. You can still include/except columns:
> tp User.first, :except => :email
ID
--
17
# a specific set of columns will entirely override the baseline config:
> tp User.first, :first_name
FIRST_NAME
----------
Phooey
tp.clear User # back to square one - print all the columns we can find
To see the config for a given object, use tp.config_for.
> tp.set User, :id, :email
> tp.config_for User
[:id, :email]
You can also set global options:
tp.set :max_width, 10 # columns won't exceed 10 characters
tp.set :time_format, "%Y" # date columns will only show the year
tp.set :capitalize_headers, false # don't capitalize column headers
You can redirect output:
f = File.open("users.txt", "w")
tp.set :io, f
tp User.all # written to users.txt instead of STDOUT
tp.clear :io
tp User.all # writing to STDOUT again
=== Multibyte
Unfortunately, the current approach to finding the width of multibyte strings is too slow. If you're going to be
working with a lot of multibyte characters, set the multibyte option first to enable the slower yet more precise
width calculation:
> tp.set :multibyte, true
== Contributing to table_print
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
* Fork the project
* Start a feature/bugfix branch
* Commit and push until you are happy with your contribution
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
== Copyright
Copyright (c) 2013 Chris Doyle. See LICENSE.txt for further details.
table_print-1.5.7/Gemfile 0000644 0000041 0000041 00000000077 13701574164 015366 0 ustar www-data www-data source 'https://rubygems.org'
gemspec :name => 'table_print'
table_print-1.5.7/features/ 0000755 0000041 0000041 00000000000 13701574164 015705 5 ustar www-data www-data table_print-1.5.7/features/printing_hash.feature 0000644 0000041 0000041 00000002622 13701574164 022121 0 ustar www-data www-data Feature: Printing hash
Scenario: A simple array of hashes
Given a variable named data with
|title | author |
|First post! | Ryan |
|Second post! | John |
|Third post! | Peter |
When I table_print data
Then the output should contain
"""
TITLE | AUTHOR
-------------|-------
First post! | Ryan
Second post! | John
Third post! | Peter
"""
Scenario: A lambda column
Given a variable named data with
|title | author |
|First post! | Ryan |
|Second post! | John |
|Third post! | Peter |
When I table_print data, [:include => {:two => lambda{|hash| hash[:author]*2}}]
Then the output should contain
"""
TITLE | AUTHOR | TWO
-------------|--------|-----------
First post! | Ryan | RyanRyan
Second post! | John | JohnJohn
Third post! | Peter | PeterPeter
"""
Scenario: A method on the object
Given a variable named data with
|title | author |
|First post! | Ryan |
|Second post! | John |
|Third post! | Peter |
When I table_print data, [:include => :size]
Then the output should contain
"""
TITLE | AUTHOR | SIZE
-------------|--------|-----
First post! | Ryan | 2
Second post! | John | 2
Third post! | Peter | 2
"""
table_print-1.5.7/features/multibyte.feature 0000644 0000041 0000041 00000001045 13701574164 021300 0 ustar www-data www-data # coding: utf-8
@ruby19
Feature: Fix the column width for multibyte character
Scenario: A simple array of hashes
Given a variable named data with
|title | author |
|これは日本語です。| 山田太郎 |
|English | Bob |
When I configure multibyte with true
When I table_print data
Then the output should contain
"""
TITLE | AUTHOR
-------------------|---------
これは日本語です。 | 山田太郎
English | Bob
"""
table_print-1.5.7/features/sensible_defaults.feature 0000644 0000041 0000041 00000007173 13701574164 022765 0 ustar www-data www-data Feature: Sensible defaults
By default, table_print shows all "getter" methods defined on your object itself. This includes anything except:
* Methods defined in an object's parent class
* Methods defined in an object's included modules
* Methods whose name ends with an equals sign (ie, setter methods)
* Methods with an arity > 0 (ie, methods that take arguments)
Scenario: A simple object
Given a class named Foo
Given Foo has attributes herp
Given Foo has a method named derp with lambda{"hurrrrr"}
Given a class named Foo::Blog
Given Foo::Blog has attributes title, author
Given Foo::Blog has a class method named foo with lambda{"just testing!"}
Given Foo::Blog has a method named two_args with lambda{|a, b| "Called with #{a}, #{b}"}
When I instantiate a Foo::Blog with {:title => "First post!", :author => 'Ryan'}
And table_print Foo::Blog
Then the output should contain
"""
TITLE | AUTHOR
------------|-------
First post! | Ryan
"""
Scenario: An array of objects
Given a class named Post
Given Post has attributes title, author
Given Post has a class method named foo with lambda{"just testing!"}
Given Post has a method named two_args with lambda{|a, b| "Called with #{a}, #{b}"}
Given a class named Blog
Given Blog has attributes posts
When I instantiate a Blog with {:posts => []}
When I instantiate a Post with {:title => "First post!", :author => 'Ryan'} and add it to blog.posts
When I instantiate a Post with {:title => "Second post!", :author => 'Ryan'} and add it to blog.posts
When I instantiate a Post with {:title => "Third post!", :author => 'Ryan'} and add it to blog.posts
And table_print blog.posts
Then the output should contain
"""
TITLE | AUTHOR
-------------|-------
First post! | Ryan
Second post! | Ryan
Third post! | Ryan
"""
Scenario: Nested objects
Given a class named Comment
Given Comment has attributes id, username, body
Given a class named Blog
Given Blog has attributes id, comments
Given I instantiate a Blog with {:id => 1, :comments => []}
And I instantiate a Comment with {:id => 1, :username => 'chris', :body => 'once upon a time'} and add it to blog.comments
And I instantiate a Comment with {:id => 2, :username => 'joe', :body => 'once upon a time'} and add it to blog.comments
When I table_print Blog, [:id, "comments.id", "comments.username"]
Then the output should contain
"""
ID | COMMENTS.ID | COMMENTS.USERNAME
---|-------------|------------------
1 | 1 | chris
| 2 | joe
"""
Scenario: An object with column info (like an ActiveRecord object)
Given a class named ColumnInfo
Given ColumnInfo has attributes name
Given a class named Blog
Given Blog has attributes title, author
Given Blog has a class method named columns with lambda{[Sandbox::ColumnInfo.new(:name => :title)]}
When I instantiate a Blog with {:title => "First post!", :author => 'Ryan'}
And table_print Blog
Then the output should contain
"""
TITLE
-----------
First post!
"""
Scenario: An object with field info (like a Mongoid object)
Given a class named Mongoid
Given a class named Blog
Given Blog has attributes title, author
Given Blog has a method named fields with lambda{{"title" => Sandbox::Mongoid.new}}
When I instantiate a Blog with {:title => "First post!", :author => 'Ryan'}
And table_print Blog
Then the output should contain
"""
TITLE
-----------
First post!
"""
table_print-1.5.7/features/excluding_columns.feature 0000644 0000041 0000041 00000001350 13701574164 023003 0 ustar www-data www-data Feature: Excluding columns
Scenario: With the :except option
Given a class named Blog
Given Blog has attributes title, author
When I instantiate a Blog with {:title => "post!", :author => 'Ryan'}
And table_print Blog, {:except => :title}
Then the output should contain
"""
AUTHOR
------
Ryan
"""
Scenario: By specifying columns
Given a class named Blog
Given Blog has attributes title, author, url
When I instantiate a Blog with {:title => "post!", :author => 'Ryan', :url => "http://google.com"}
And table_print Blog, [:author, :url]
Then the output should contain
"""
AUTHOR | URL
-------|------------------
Ryan | http://google.com
"""
table_print-1.5.7/features/printing_struct.feature 0000644 0000041 0000041 00000002661 13701574164 022525 0 ustar www-data www-data Feature: Printing structs
Scenario: A simple array of structs
Given an array of structs named data with
|title | author |
|First post! | Ryan |
|Second post! | John |
|Third post! | Peter |
When I table_print data
Then the output should contain
"""
TITLE | AUTHOR
-------------|-------
First post! | Ryan
Second post! | John
Third post! | Peter
"""
Scenario: A lambda column
Given an array of structs named data with
|title | author |
|First post! | Ryan |
|Second post! | John |
|Third post! | Peter |
When I table_print data, [:include => {:two => lambda{|hash| hash[:author]*2}}]
Then the output should contain
"""
TITLE | AUTHOR | TWO
-------------|--------|-----------
First post! | Ryan | RyanRyan
Second post! | John | JohnJohn
Third post! | Peter | PeterPeter
"""
Scenario: A method on the object
Given an array of structs named data with
|title | author |
|First post! | Ryan |
|Second post! | John |
|Third post! | Peter |
When I table_print data, [:include => :size]
Then the output should contain
"""
TITLE | AUTHOR | SIZE
-------------|--------|-----
First post! | Ryan | 2
Second post! | John | 2
Third post! | Peter | 2
"""
table_print-1.5.7/features/configuring_output.feature 0000644 0000041 0000041 00000007614 13701574164 023224 0 ustar www-data www-data Feature: Configuring output
Scenario: Setting a (max? or specific?) width for all columns
Scenario: Setting a specific width for an individual column
Given a class named Blog
Given Blog has attributes title, author
When I instantiate a Blog with {:title => "post!", :author => 'Ryan Ryan Ryan Ryan Ryan Ryan Ryan Ryan Ryan Ryan Ryan Ryan Ryan'}
And table_print Blog, {:include => {:author => {:width => 40}}}
Then the output should contain
"""
TITLE | AUTHOR
------|-----------------------------------------
post! | Ryan Ryan Ryan Ryan Ryan Ryan Ryan Ry...
"""
Scenario: Setting a minimum width for an individual column
Given a class named Blog
Given Blog has attributes title, author
When I instantiate a Blog with {:title => "post!", :author => 'Ryan Ryan'}
And table_print Blog, {:include => {:author => {:min_width => 40}}}
Then the output should contain
"""
TITLE | AUTHOR
------|-----------------------------------------
post! | Ryan Ryan
"""
Scenario: Setting a fixed width for an individual column, when data width is greater than fixed width
Given a class named Blog
Given Blog has attributes title, author
When I instantiate a Blog with {:title => "post!", :author => 'Ryan Ryan Ryan Ryan Ryan Ryan Ryan'}
And table_print Blog, {:include => {:author => {:fixed_width => 15}}}
Then the output should contain
"""
TITLE | AUTHOR
------|----------------
post! | Ryan Ryan Ry...
"""
Scenario: Setting a fixed width for an individual column, when data width is less than fixed width
Given a class named Blog
Given Blog has attributes title, author
When I instantiate a Blog with {:title => "post!", :author => 'Ryan Ryan'}
And table_print Blog, {:include => {:author => {:fixed_width => 15}}}
Then the output should contain
"""
TITLE | AUTHOR
------|----------------
post! | Ryan Ryan
"""
Scenario: Specifying configuration on a per-object basis
Given a class named Blog
Given Blog has attributes title, author
When I instantiate a Blog with {:title => "post!", :author => 'Ryan'}
And configure Blog with :title
And table_print Blog
Then the output should contain
"""
TITLE
-----
post!
"""
Scenario: Specifying configuration on a per-object basis with an included column
Given a class named Blog
Given Blog has attributes title, author
When I instantiate a Blog with {:title => "post!", :author => 'Ryan'}
And configure Blog with {:include => {:foobar => lambda{|b| b.title}}}
And table_print Blog
Then the output should contain
"""
TITLE | AUTHOR | FOOBAR
------|--------|-------
post! | Ryan | post!
"""
Scenario: Applying a formatter
Scenario: Setting a column name
Given a class named Blog
Given Blog has attributes title, author
When I instantiate a Blog with {:title => "post!", :author => 'Ryan'}
And table_print Blog, {:wombat => {:display_method => :author}}
Then the output should contain
"""
WOMBAT
------
Ryan
"""
Scenario: Setting a lowercase column name
Given a class named Blog
Given Blog has attributes title, author
When I instantiate a Blog with {:title => "post!", :author => 'Ryan'}
And I configure capitalize_headers with false
And table_print Blog, {:author => {:display_name => "Wom Bat"}}
Then the output should contain
"""
Wom Bat
-------
Ryan
"""
Scenario: Setting the column separator
Given a class named Blog
Given Blog has attributes title, author
When I instantiate a Blog with {:title => "post!", :author => 'Ryan'}
And I configure separator with ','
And table_print Blog
Then the output should contain
"""
TITLE , AUTHOR
------,-------
post! , Ryan
"""
table_print-1.5.7/features/adding_columns.feature 0000644 0000041 0000041 00000003156 13701574164 022255 0 ustar www-data www-data Feature: Adding columns
Scenario: With the :include option
Given a class named Foo
Given Foo has attributes herp, blog
Given a class named Foo::Blog
Given Foo::Blog has attributes title, author
Given Foo::Blog has a class method named foo with lambda{"just testing!"}
Given Foo::Blog has a method named two_args with lambda{|a, b| "Called with #{a}, #{b}"}
When I instantiate a Foo with {:herp => "derp"}
When I instantiate a Foo::Blog with {:title => "post!", :author => 'Ryan'} and assign it to foo.blog
And table_print Foo, {:include => ["blog.author", "blog.title"]}
Then the output should contain
"""
HERP | BLOG.AUTHOR | BLOG.TITLE
-----|-------------|-----------
derp | Ryan | post!
"""
Scenario: Providing a named proc
Given a class named Blog
Given Blog has attributes title, author
When I instantiate a Blog with {:title => "post!", :author => 'Ryan'}
And table_print Blog, {:wombat => {:display_method => lambda{|blog| blog.author.gsub(/[aeiou]/, "").downcase}}}
Then the output should contain
"""
WOMBAT
------
ryn
"""
Scenario: Providing a named proc without saying 'display_method', eg :foo => lambda{}
Given a class named Blog
Given Blog has attributes title, author
When I instantiate a Blog with {:title => "post!", :author => 'Ryan'}
And table_print Blog, {:wombat => lambda{|blog| blog.author.gsub(/[aeiou]/, "").downcase}}
Then the output should contain
"""
WOMBAT
------
ryn
"""
Scenario: Using a proc as a filter (ie, overriding an existing column with a proc)
table_print-1.5.7/features/support/ 0000755 0000041 0000041 00000000000 13701574164 017421 5 ustar www-data www-data table_print-1.5.7/features/support/step_definitions/ 0000755 0000041 0000041 00000000000 13701574164 022767 5 ustar www-data www-data table_print-1.5.7/features/support/step_definitions/before.rb 0000644 0000041 0000041 00000000170 13701574164 024554 0 ustar www-data www-data Before do
Sandbox.cleanup!
TablePrint::Config.clear(:capitalize_headers)
TablePrint::Config.clear(:separator)
end
table_print-1.5.7/features/support/step_definitions/steps.rb 0000644 0000041 0000041 00000005734 13701574164 024463 0 ustar www-data www-data require 'cat'
require 'ostruct'
require 'table_print'
Given /^a class named (.*)$/ do |klass|
Sandbox.add_class(klass)
end
Given /^(.*) has attributes (.*)$/ do |klass, attributes|
attrs = attributes.split(",").map { |attr| attr.strip }
Sandbox.add_attributes(klass, *attrs)
end
Given /^(.*) has a class method named (.*) with (.*)$/ do |klass, method_name, blk|
Sandbox.add_class_method(klass, method_name, &eval(blk))
end
Given /^(.*) has a method named (\w*) with (.*)$/ do |klass, method_name, blk|
Sandbox.add_method(klass, method_name, &eval(blk))
end
Given /^a variable named (.*) with$/ do |variable, table|
@objs ||= OpenStruct.new
@objs.send("#{variable.downcase}=", table.hashes)
end
Given /^an array of structs named (.*) with$/ do |variable, table|
@objs ||= OpenStruct.new
struct = Struct.new(*(table.column_names.collect(&:to_sym)))
data = table.hashes.collect do |hsh|
obj = struct.new
hsh.each {|k,v| obj.send "#{k}=", v}
obj
end
@objs.send("#{variable.downcase}=", data)
end
When /^I instantiate a (.*) with (\{.*\})$/ do |klass, args|
@objs ||= OpenStruct.new
@objs.send("#{klass.downcase}=", Sandbox.const_get_from_string(klass).new(eval(args)))
end
When /^I instantiate a (.*) with (\{.*\}) and (add it|assign it) to (.*)$/ do |klass, args, assignment_method, target|
# the thing we're instantiating
child = Sandbox.const_get_from_string(klass).new(eval(args))
# the place we're going to add it
method_chain = target.split(".")
target_method = method_chain.pop
target_object = method_chain.inject(@objs) { |obj, method_name| obj.send(method_name) }
# how we're going to add it
if assignment_method == "assign it"
target_object.send("#{target_method}=", child)
else
target_object.send("#{target_method}") << child
end
end
When /^I configure multibyte with (.*)$/ do |value|
TablePrint::Config.set(:multibyte, [value == "true"])
end
When /^I configure capitalize_headers with (.*)$/ do |value|
TablePrint::Config.set(:capitalize_headers, [value == "true"])
end
When /^I configure separator with '(.*)'$/ do |value|
TablePrint::Config.set(:separator, [value])
end
When /^configure (.*) with (.*)$/ do |klass, config|
klass = Sandbox.const_get_from_string(klass)
TablePrint::Config.set(klass, eval(config))
end
When /table_print ([\w:]*), (.*)$/ do |klass, options|
tp(@objs.send(klass.downcase), eval(options))
end
When /table_print ([\w\.:]*)$/ do |klass|
obj = @objs.send(klass.split(".").first.downcase)
obj = obj.send(klass.split(".").last) if klass.include? "." # hack - we're assuming only two levels. use inject to find the target.
tp(obj)
end
Then /^the output should contain$/ do |string|
output = @r.each_line.to_a
output.zip(string.split("\n")).each do |actual, expected|
actual.gsub(/\s/m, "").split(//).sort.join.should == expected.gsub(" ", "").split(//).sort.join
end
end
def tp(data, options={})
@r, w = IO.pipe
w.puts TablePrint::Printer.table_print(data, options)
w.close
end
table_print-1.5.7/LICENSE.txt 0000644 0000041 0000041 00000002044 13701574164 015712 0 ustar www-data www-data Copyright (c) 2012-2016 Chris Doyle
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.