table_print-1.5.6/0000755000004100000410000000000012675771556014103 5ustar www-datawww-datatable_print-1.5.6/Rakefile0000644000004100000410000000222312675771556015547 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.6/Gemfile0000644000004100000410000000007712675771556015402 0ustar www-datawww-datasource 'https://rubygems.org' gemspec :name => 'table_print' table_print-1.5.6/features/0000755000004100000410000000000012675771556015721 5ustar www-datawww-datatable_print-1.5.6/features/printing_struct.feature0000644000004100000410000000266112675771556022541 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.6/features/printing_hash.feature0000644000004100000410000000262212675771556022135 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.6/features/multibyte.feature0000644000004100000410000000104512675771556021314 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.6/features/excluding_columns.feature0000644000004100000410000000135012675771556023017 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.6/features/configuring_output.feature0000644000004100000410000000761412675771556023240 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.6/features/adding_columns.feature0000644000004100000410000000315612675771556022271 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.6/features/support/0000755000004100000410000000000012675771556017435 5ustar www-datawww-datatable_print-1.5.6/features/support/step_definitions/0000755000004100000410000000000012675771556023003 5ustar www-datawww-datatable_print-1.5.6/features/support/step_definitions/steps.rb0000644000004100000410000000573012675771556024473 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.lines.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.6/features/support/step_definitions/before.rb0000644000004100000410000000017012675771556024570 0ustar www-datawww-dataBefore do Sandbox.cleanup! TablePrint::Config.clear(:capitalize_headers) TablePrint::Config.clear(:separator) end table_print-1.5.6/features/sensible_defaults.feature0000644000004100000410000000717312675771556023001 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.6/.rspec0000644000004100000410000000001012675771556015207 0ustar www-datawww-data--color table_print-1.5.6/LICENSE.txt0000644000004100000410000000204412675771556015726 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. table_print-1.5.6/spec/0000755000004100000410000000000012675771556015035 5ustar www-datawww-datatable_print-1.5.6/spec/hash_extensions_spec.rb0000644000004100000410000000122512675771556021576 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.6/spec/config_resolver_spec.rb0000644000004100000410000002133612675771556021567 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.6/spec/config_spec.rb0000644000004100000410000000405412675771556017644 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.6/spec/returnable_spec.rb0000644000004100000410000000165612675771556020547 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.6/spec/printable_spec.rb0000644000004100000410000000325412675771556020360 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.6/spec/row_group_spec.rb0000644000004100000410000004244612675771556020431 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.6/spec/spec_helper.rb0000644000004100000410000000025112675771556017651 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.6/spec/table_print_spec.rb0000644000004100000410000000622312675771556020702 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" do Printer.new([]).message.should be_a Numeric 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.6/spec/column_spec.rb0000644000004100000410000000444712675771556017702 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.6/spec/fingerprinter_spec.rb0000644000004100000410000001762712675771556021267 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.6/spec/formatter_spec.rb0000644000004100000410000000322312675771556020377 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.6/README.rdoc0000644000004100000410000002033312675771556015712 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.6/.travis.yml0000644000004100000410000000013312675771556016211 0ustar  www-datawww-datalanguage: ruby
rvm:
  - 2.0
  - 2.2.4
  - jruby-18mode
  - jruby-19mode
  - rbx-2
  - ree

table_print-1.5.6/lib/0000755000004100000410000000000012675771556014651 5ustar  www-datawww-datatable_print-1.5.6/lib/table_print.rb0000644000004100000410000000413512675771556017504 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
    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.6/lib/table_print/0000755000004100000410000000000012675771556017154 5ustar  www-datawww-datatable_print-1.5.6/lib/table_print/returnable.rb0000644000004100000410000000100512675771556021640 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.6/lib/table_print/row_group.rb0000644000004100000410000001360212675771556021526 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 |value, formatter|
        formatter.format(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.6/lib/table_print/formatter.rb0000644000004100000410000000175412675771556021513 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.6/lib/table_print/fingerprinter.rb0000644000004100000410000000553612675771556022370 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.6/lib/table_print/hash_extensions.rb0000644000004100000410000000147612675771556022713 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.6/lib/table_print/column.rb0000644000004100000410000000266512675771556021007 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.6/lib/table_print/version.rb0000644000004100000410000000005312675771556021164 0ustar  www-datawww-datamodule TablePrint
  VERSION = "1.5.6"
end

table_print-1.5.6/lib/table_print/config.rb0000644000004100000410000000334112675771556020747 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.6/lib/table_print/printable.rb0000644000004100000410000000202112675771556021454 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.6/lib/table_print/config_resolver.rb0000644000004100000410000000522112675771556022667 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.6/.gitignore0000644000004100000410000000140712675771556016075 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.6/table_print.gemspec0000644000004100000410000000220512675771556017752 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', '~> 1.2.1'
  gem.add_development_dependency 'rspec', '~> 2.11.0'
  gem.add_development_dependency 'rake', '~> 0.9.2'
end
table_print-1.5.6/.document0000644000004100000410000000006712675771556015725 0ustar  www-datawww-datalib/**/*.rb
bin/*
- 
features/**/*.feature
LICENSE.txt