Skip to content

Instantly share code, notes, and snippets.

@jacaetevha
Last active January 12, 2016 22:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save jacaetevha/8660646 to your computer and use it in GitHub Desktop.
Save jacaetevha/8660646 to your computer and use it in GitHub Desktop.
Prints (roughly) 5x7 cards on a standard 8.5x11 sheet of paper with the names of tables at the top, and columns of column data. The column data printed is just the name and its type. Column types have been abbreviated due to space constraints.
# This script creates blocks of text on 8.5x11 paper that is suitable for
# cutting and pasting onto 5x7 index cards. The blocks of text are descriptions
# of your Rails database tables. Specifically, it prints out a representation
# of the column type next to the name of a column for each table.
#
# It attempts to layout cards smartly so that large tables are handled with
# some gracefulness. For instance, this script was tested with tables that
# have as few as 3 columns and tables up to 134 columns. If a table has more
# columns in it than can be easily represented on one 5x7 index card, it will
# use a full 8.5x11 page.
#
# Run this file with the "rails runner" command so that it has access to your
# database.
#
# rails runner location/to/database_cards.rb
require "rubygems"
require "prawn"
require "bundler/setup"
module Card
TYPES = {
"integer" => "INT",
"float" => "FLT",
"datetime" => "D/T",
"date" => "DATE",
"boolean" => "BOOL",
"string" => "STR",
"text" => "TXT",
"int4range" => "RNG",
"hstore" => "HSTR"
}
COMFORTABLE_ROW_COUNT = 23
TIGHT_ROW_COUNT = 28
SPACEY_ROW_COUNT = 19
CARD_WIDTH = 72 * 7 # 7 inches
CARD_HEIGHT = 72 * 5 # 5 inches
class AbstractCard
attr_reader :table_name, :columns, :body
def initialize(table_name, columns)
@types_size = TYPES.values.max_by(&:size).size
@table_name = table_name
@columns = columns_as_string columns
@body = @columns.join("\n")
end
def boxes_per_page
height > CARD_HEIGHT ? 2 : 1
end
def db_column_count
@columns.size
end
def draw_on(doc, width=doc.bounds.width)
doc.column_box([0, doc.cursor], columns: card_columns, width: width, height: height - 50) do
doc.text body, size: font_size, align: :left, leading: 0, inline_format: true
end
end
def height
if @columns.size > (TIGHT_ROW_COUNT * 4)
CARD_HEIGHT * 2
else
CARD_HEIGHT
end
end
def title
"#{table_name.upcase} <sup>(#{db_column_count})</sup>"
end
def width
CARD_WIDTH
end
protected
def max_db_column_name_size
@columns.max_by(&:size).size
end
private
def columns_as_string(columns)
columns.map do |column|
type = (TYPES[column.type.to_s] || "??#{column.type}").ljust(@types_size, " ")
"<font name='Courier'><strong><sup><color rgb='AB66FF'>#{type} </color></sup></strong></font>#{column.name}"
end
end
end
class OneColumnCard < AbstractCard
def card_columns
1
end
def font_size
max_db_column_name_size > 28 ? 10 : 12
end
end
class TwoColumnCard < OneColumnCard
def card_columns
2
end
end
class ThreeColumnCard < AbstractCard
def card_columns
3
end
def font_size
10
end
end
class ManyColumnCard < AbstractCard
ROW_COUNT_FOR_3_COLS = 51
def card_columns
db_column_count > (ROW_COUNT_FOR_3_COLS * 3) ? 4 : 3
end
def font_size
(card_columns == 4) ? (max_db_column_name_size > 27 ? 6 : 8) : 10
end
end
def self.create(table_name, columns)
if columns.size <= SPACEY_ROW_COUNT
OneColumnCard.new table_name, columns
elsif columns.size <= COMFORTABLE_ROW_COUNT * 2
TwoColumnCard.new table_name, columns
elsif columns.size <= COMFORTABLE_ROW_COUNT * 3
ThreeColumnCard.new table_name, columns
else
ManyColumnCard.new table_name, columns
end
end
end
class Prawn::Document
SPACE_BETWEEN_COLUMN_NAMES = 2
def self.generate_cards(outfile, cards)
generate(outfile, page_layout: :portrait) do
row = 2
cards.each do |card|
if row == 0
start_new_page
row = 2
end
row -= draw_card(card, row)
end
end
end
def margin_box(margin, &block)
bounding_box [bounds.left + margin, bounds.top - margin],
width: bounds.width - (margin * 2), height: bounds.height - (margin * 2),
&block
end
def outline_box
stroke_rectangle bounds.top_left, bounds.width, bounds.height
end
def draw_card(card, row)
y_offset, height = if card.boxes_per_page > 1
[bounds.height, card.height]
else
# here, we offset the height by a little bit in order to separate the bounding boxes
[(card.height * row + ((bounds.height - (2 * card.height))/2)), card.height - 10]
end
bounding_box [0, y_offset], width: card.width, height: height do
stroke_color '000000'
outline_box
margin_box 8 do
font "Helvetica"
text card.title, size: 18, inline_format: true
move_down 2
stroke do
stroke_color '666666'
horizontal_rule
end
margin_box 12 do
font "Times-Roman"
move_down 20
card.draw_on self
end
end
end
card.boxes_per_page
end
end
def run
connection = ActiveRecord::Base.connection
cards = (connection.tables - ['schema_migrations']).sort!.map! do |table_name|
Card.create table_name, connection.columns(table_name)
end
file = ARGV[0] || 'tmp/cards.pdf'
Prawn::Document.generate_cards(file, cards)
puts "cards written to #{file}"
end
run
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment