table_print-1.5.7/0000755000004100000410000000000013701574164014067 5ustar www-datawww-datatable_print-1.5.7/.travis.yml0000644000004100000410000000012713701574164016200 0ustar www-datawww-datalanguage: ruby rvm: - 2.4 - 2.5 - 2.6 - 2.7 - jruby-18mode - jruby-19mode table_print-1.5.7/.rspec0000644000004100000410000000001013701574164015173 0ustar www-datawww-data--color table_print-1.5.7/spec/0000755000004100000410000000000013701574164015021 5ustar www-datawww-datatable_print-1.5.7/spec/printable_spec.rb0000644000004100000410000000325413701574164020344 0ustar www-datawww-datarequire '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.rb0000644000004100000410000001762713701574164021253 0ustar www-datawww-datarequire '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.rb0000644000004100000410000000405413701574164017630 0ustar www-datawww-datarequire '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.rb0000644000004100000410000000635013701574164020667 0ustar www-datawww-datarequire '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.rb0000644000004100000410000000165613701574164020533 0ustar www-datawww-datarequire '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.rb0000644000004100000410000000122513701574164021562 0ustar www-datawww-datarequire '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.rb0000644000004100000410000002133613701574164021553 0ustar www-datawww-datarequire '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.rb0000644000004100000410000000322313701574164020363 0ustar www-datawww-datarequire '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.rb0000644000004100000410000000444713701574164017666 0ustar www-datawww-datarequire '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.rb0000644000004100000410000004244613701574164020415 0ustar www-datawww-datarequire '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.rb0000644000004100000410000000025113701574164017635 0ustar www-datawww-datarequire '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/.gitignore0000644000004100000410000000140713701574164016061 0ustar www-datawww-dataGemfile.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/.document0000644000004100000410000000006713701574164015711 0ustar www-datawww-datalib/**/*.rb bin/* - features/**/*.feature LICENSE.txt table_print-1.5.7/table_print.gemspec0000644000004100000410000000220513701574164017736 0ustar www-datawww-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/Rakefile0000644000004100000410000000222313701574164015533 0ustar www-datawww-datarequire '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/0000755000004100000410000000000013701574164014635 5ustar www-datawww-datatable_print-1.5.7/lib/table_print/0000755000004100000410000000000013701574164017140 5ustar www-datawww-datatable_print-1.5.7/lib/table_print/formatter.rb0000644000004100000410000000175413701574164021477 0ustar www-datawww-datamodule 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.rb0000644000004100000410000000005313701574164021150 0ustar www-datawww-datamodule TablePrint VERSION = "1.5.7" end table_print-1.5.7/lib/table_print/column.rb0000644000004100000410000000266513701574164020773 0ustar www-datawww-datamodule 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.rb0000644000004100000410000000100413701574164021623 0ustar www-datawww-datamodule 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.rb0000644000004100000410000000553613701574164022354 0ustar www-datawww-datamodule 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.rb0000644000004100000410000000147613701574164022677 0ustar www-datawww-datamodule 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.rb0000644000004100000410000001361613701574164021517 0ustar www-datawww-datamodule 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.rb0000644000004100000410000000522113701574164022653 0ustar www-datawww-datamodule 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.rb0000644000004100000410000000202113701574164021440 0ustar www-datawww-datamodule 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.rb0000644000004100000410000000334113701574164020733 0ustar www-datawww-datamodule 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.rb0000644000004100000410000000442213701574164017467 0ustar www-datawww-datarequire '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.rdoc0000644000004100000410000002033313701574164015676 0ustar www-datawww-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/Gemfile0000644000004100000410000000007713701574164015366 0ustar  www-datawww-datasource 'https://rubygems.org'

gemspec :name => 'table_print'

table_print-1.5.7/features/0000755000004100000410000000000013701574164015705 5ustar  www-datawww-datatable_print-1.5.7/features/printing_hash.feature0000644000004100000410000000262213701574164022121 0ustar  www-datawww-dataFeature: 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.feature0000644000004100000410000000104513701574164021300 0ustar  www-datawww-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.feature0000644000004100000410000000717313701574164022765 0ustar  www-datawww-dataFeature: 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.feature0000644000004100000410000000135013701574164023003 0ustar  www-datawww-dataFeature: 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.feature0000644000004100000410000000266113701574164022525 0ustar  www-datawww-dataFeature: 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.feature0000644000004100000410000000761413701574164023224 0ustar  www-datawww-dataFeature: 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.feature0000644000004100000410000000315613701574164022255 0ustar  www-datawww-dataFeature: 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/0000755000004100000410000000000013701574164017421 5ustar  www-datawww-datatable_print-1.5.7/features/support/step_definitions/0000755000004100000410000000000013701574164022767 5ustar  www-datawww-datatable_print-1.5.7/features/support/step_definitions/before.rb0000644000004100000410000000017013701574164024554 0ustar  www-datawww-dataBefore do
  Sandbox.cleanup!
  TablePrint::Config.clear(:capitalize_headers)
  TablePrint::Config.clear(:separator)
end
table_print-1.5.7/features/support/step_definitions/steps.rb0000644000004100000410000000573413701574164024463 0ustar  www-datawww-datarequire '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.txt0000644000004100000410000000204413701574164015712 0ustar  www-datawww-dataCopyright (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.