table_setter 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. data/.document +5 -0
  2. data/.gitignore +20 -0
  3. data/LICENSE +20 -0
  4. data/README +10 -0
  5. data/Rakefile +66 -0
  6. data/TODO +3 -0
  7. data/VERSION.yml +5 -0
  8. data/bin/table-setter +5 -0
  9. data/doc/TableFu/Formatting.html +178 -0
  10. data/doc/TableSetter/App.html +176 -0
  11. data/doc/TableSetter/Command.html +376 -0
  12. data/doc/TableSetter/Table.html +1813 -0
  13. data/doc/TableSetter.html +292 -0
  14. data/doc/_index.html +146 -0
  15. data/doc/class_list.html +36 -0
  16. data/doc/css/common.css +1 -0
  17. data/doc/css/full_list.css +50 -0
  18. data/doc/css/style.css +268 -0
  19. data/doc/file.README.html +68 -0
  20. data/doc/file_list.html +38 -0
  21. data/doc/frames.html +13 -0
  22. data/doc/index.html +68 -0
  23. data/doc/js/app.js +99 -0
  24. data/doc/js/full_list.js +106 -0
  25. data/doc/js/jquery.js +19 -0
  26. data/doc/method_list.html +291 -0
  27. data/doc/top-level-namespace.html +85 -0
  28. data/documentation/css/dawn.css +121 -0
  29. data/documentation/css/styles.css +63 -0
  30. data/documentation/images/folder.png +0 -0
  31. data/documentation/images/key.png +0 -0
  32. data/documentation/images/proplogo.png +0 -0
  33. data/documentation/images/publish.png +0 -0
  34. data/documentation/images/text-x-generic.png +0 -0
  35. data/documentation/index.html.erb +221 -0
  36. data/documentation/tables/example/index.html +4074 -0
  37. data/documentation/tables/example_faceted/index.html +17239 -0
  38. data/documentation/tables/example_formatted/index.html +861 -0
  39. data/documentation/tables/example_local/1/index.html +6084 -0
  40. data/documentation/tables/example_local/10/index.html +6084 -0
  41. data/documentation/tables/example_local/11/index.html +6084 -0
  42. data/documentation/tables/example_local/12/index.html +6084 -0
  43. data/documentation/tables/example_local/13/index.html +6084 -0
  44. data/documentation/tables/example_local/14/index.html +6084 -0
  45. data/documentation/tables/example_local/15/index.html +6084 -0
  46. data/documentation/tables/example_local/16/index.html +6084 -0
  47. data/documentation/tables/example_local/17/index.html +6084 -0
  48. data/documentation/tables/example_local/18/index.html +6084 -0
  49. data/documentation/tables/example_local/19/index.html +6084 -0
  50. data/documentation/tables/example_local/2/index.html +6084 -0
  51. data/documentation/tables/example_local/20/index.html +6084 -0
  52. data/documentation/tables/example_local/21/index.html +6084 -0
  53. data/documentation/tables/example_local/22/index.html +6084 -0
  54. data/documentation/tables/example_local/23/index.html +6084 -0
  55. data/documentation/tables/example_local/24/index.html +1404 -0
  56. data/documentation/tables/example_local/3/index.html +6084 -0
  57. data/documentation/tables/example_local/4/index.html +6084 -0
  58. data/documentation/tables/example_local/5/index.html +6084 -0
  59. data/documentation/tables/example_local/6/index.html +6084 -0
  60. data/documentation/tables/example_local/7/index.html +6084 -0
  61. data/documentation/tables/example_local/8/index.html +6084 -0
  62. data/documentation/tables/example_local/9/index.html +6084 -0
  63. data/documentation/tables/example_local/index.html +6084 -0
  64. data/documentation/tables/favicon.ico +0 -0
  65. data/documentation/tables/images/th_arrow_asc.gif +0 -0
  66. data/documentation/tables/images/th_arrow_desc.gif +0 -0
  67. data/documentation/tables/index.html +48 -0
  68. data/documentation/tables/javascripts/application.js +32 -0
  69. data/documentation/tables/javascripts/jquery.tablesorter.js +852 -0
  70. data/documentation/tables/javascripts/jquery.tablesorter.multipagefilter.js +111 -0
  71. data/documentation/tables/javascripts/jquery.tablesorter.pager.js +183 -0
  72. data/documentation/tables/stylesheets/stylesheet.css +67 -0
  73. data/index.html +238 -0
  74. data/lib/table_setter/app.rb +54 -0
  75. data/lib/table_setter/command.rb +139 -0
  76. data/lib/table_setter/table.rb +226 -0
  77. data/lib/table_setter.rb +32 -0
  78. data/spec/spec.opts +1 -0
  79. data/spec/spec_helper.rb +11 -0
  80. data/spec/table-setter-app_spec.rb +24 -0
  81. data/spec/table-setter_spec.rb +179 -0
  82. data/table_setter.gemspec +171 -0
  83. data/template/config.ru +33 -0
  84. data/template/public/favicon.ico +0 -0
  85. data/template/public/images/th_arrow_asc.gif +0 -0
  86. data/template/public/images/th_arrow_desc.gif +0 -0
  87. data/template/public/javascripts/application.js +32 -0
  88. data/template/public/javascripts/jquery.tablesorter.js +852 -0
  89. data/template/public/javascripts/jquery.tablesorter.multipagefilter.js +111 -0
  90. data/template/public/javascripts/jquery.tablesorter.pager.js +183 -0
  91. data/template/public/stylesheets/stylesheet.css +74 -0
  92. data/template/tables/example.yml +21 -0
  93. data/template/tables/example_faceted.yml +27 -0
  94. data/template/tables/example_formatted.csv +1 -0
  95. data/template/tables/example_formatted.yml +27 -0
  96. data/template/tables/example_local.csv +5806 -0
  97. data/template/tables/example_local.yml +27 -0
  98. data/template/views/404.erb +4 -0
  99. data/template/views/500.erb +4 -0
  100. data/template/views/index.erb +7 -0
  101. data/template/views/layout.erb +34 -0
  102. data/template/views/table.erb +78 -0
  103. metadata +240 -0
@@ -0,0 +1,111 @@
1
+ (function($){
2
+ $.extend({
3
+ tablesorterMultiPageFilter : new function(){
4
+
5
+ function replaceRows(table){
6
+ // clear the table body
7
+ $.tablesorter.clearTableBody(table);
8
+ var tableBody = $(table.tBodies[0]);
9
+
10
+ for(var i = 0; i < table.config.collection.length; i++) {
11
+ var o = table.config.collection[i];
12
+ var l = o.length;
13
+ for(var j=0; j < l; j++) {
14
+ tableBody[0].appendChild(o[j]);
15
+ }
16
+ }
17
+ $(table).trigger("applyWidgets");
18
+ }
19
+
20
+
21
+ function renderTable(table){
22
+ var newString = table.config.filterSelector[0].value;
23
+ if(newString.length > 1){
24
+ if(table.config.container){
25
+ table.config.container.hide();
26
+ }
27
+
28
+ var toShow = [];
29
+ newString = $.trim(newString);
30
+ var words = newString.toLowerCase().split(" ");
31
+
32
+ // no change, don't do anything
33
+ if (newString === table.config.string) return false;
34
+ // press was just a string
35
+ if (newString[-1] === " ") { table.config.string = newString; return false; }
36
+ // most of the string is old
37
+ if (newString.indexOf(table.config.string) > -1){
38
+ // don't change the search array but we only need the last word
39
+ words = [words.pop()];
40
+ } else {
41
+ // we need to search all rows
42
+ table.config.collection = table.config.rowsCopy.slice(0);
43
+ };
44
+
45
+ // split out words
46
+ len = table.config.collection.length;
47
+ var counter = 0;
48
+ for(var j = 0; j < len; j++ ){
49
+ var text = table.config.collection[j].text().toLowerCase();
50
+ for (var i=0; i < words.length; i++) {
51
+ if (text.indexOf(words[i]) > -1) {
52
+ toShow.push(table.config.collection[j]);
53
+ }
54
+ }
55
+ counter++;
56
+ };
57
+ table.config.collection = toShow.slice(0);
58
+ replaceRows(table);
59
+ table.config.string = newString;
60
+ } else {
61
+ table.config.string = "";
62
+ table.config.collection = table.config.rowsCopy.slice(0);
63
+ if(table.config.container){
64
+ table.config.container.show();
65
+ } else {
66
+ replaceRows(table);
67
+ }
68
+ }
69
+ }
70
+
71
+ this.defaults = {
72
+ filterSelector: $("#filter")
73
+ }
74
+
75
+ this.init = function(settings) {
76
+ return this.each(function(){
77
+ config = $.extend(this.config, $.tablesorterMultiPageFilter.defaults, settings);
78
+
79
+ var table = this,
80
+ filter = config.filterSelector
81
+ ;
82
+
83
+ // save old appenders and define a new one that grabs the row cache and saves it
84
+ if(!table.config.rowsCopy){
85
+ var oldAppend = config.appender || function (table, rows) {};
86
+ this.config.appender = function(table, rows){
87
+ table.config.rowsCopy = rows;
88
+ oldAppend(table, rows);
89
+ }
90
+ $(this).trigger("appendCache");
91
+ }
92
+ table.config.string = "";
93
+ table.config.collection = [];
94
+ table.config.collection = table.config.rowsCopy.slice(0)
95
+
96
+ function filterMe(){
97
+ renderTable(table);
98
+ return false;
99
+ }
100
+ $(filter).keyup(filterMe);
101
+ $(filter).submit(filterMe);
102
+ });
103
+ }
104
+ }
105
+ });
106
+
107
+
108
+ $.fn.extend({
109
+ tablesorterMultiPageFilter: $.tablesorterMultiPageFilter.init
110
+ });
111
+ }(jQuery))
@@ -0,0 +1,183 @@
1
+ (function($) {
2
+ $.extend({
3
+ tablesorterPager: new function() {
4
+
5
+ function updatePageDisplay(c) {
6
+ var s = $(c.cssPageDisplay,c.container).text((c.page+1) + c.seperator + c.totalPages);
7
+ }
8
+
9
+ function setPageSize(table,size) {
10
+ var c = table.config;
11
+ c.size = size;
12
+ c.totalPages = Math.ceil(c.totalRows / c.size);
13
+ c.pagerPositionSet = false;
14
+ moveToPage(table);
15
+ fixPosition(table);
16
+ }
17
+
18
+ function fixPosition(table) {
19
+ var c = table.config;
20
+ if(!c.pagerPositionSet && c.positionFixed) {
21
+ var c = table.config, o = $(table);
22
+ if(o.offset) {
23
+ c.container.css({
24
+ top: o.offset().top + o.height() + 'px',
25
+ position: 'absolute'
26
+ });
27
+ }
28
+ c.pagerPositionSet = true;
29
+ }
30
+ }
31
+
32
+ function moveToFirstPage(table) {
33
+ var c = table.config;
34
+ c.page = 0;
35
+ moveToPage(table);
36
+ }
37
+
38
+ function moveToLastPage(table) {
39
+ var c = table.config;
40
+ c.page = (c.totalPages-1);
41
+ moveToPage(table);
42
+ }
43
+
44
+ function moveToNextPage(table) {
45
+ var c = table.config;
46
+ c.page++;
47
+ if(c.page >= (c.totalPages-1)) {
48
+ c.page = (c.totalPages-1);
49
+ }
50
+ moveToPage(table);
51
+ }
52
+
53
+ function moveToPrevPage(table) {
54
+ var c = table.config;
55
+ c.page--;
56
+ if(c.page <= 0) {
57
+ c.page = 0;
58
+ }
59
+ moveToPage(table);
60
+ }
61
+
62
+
63
+ function moveToPage(table) {
64
+ var c = table.config;
65
+ if(c.page < 0 || c.page > (c.totalPages-1)) {
66
+ c.page = 0;
67
+ }
68
+
69
+ renderTable(table,c.rowsCopy);
70
+ }
71
+
72
+ function renderTable(table,rows) {
73
+
74
+ var c = table.config;
75
+ var l = rows.length;
76
+ var s = (c.page * c.size);
77
+ var e = (s + c.size);
78
+ if(e > rows.length ) {
79
+ e = rows.length;
80
+ }
81
+
82
+
83
+ var tableBody = $(table.tBodies[0]);
84
+
85
+ // clear the table body
86
+
87
+ $.tablesorter.clearTableBody(table);
88
+
89
+ for(var i = s; i < e; i++) {
90
+
91
+ //tableBody.append(rows[i]);
92
+
93
+ var o = rows[i];
94
+ var l = o.length;
95
+ for(var j=0; j < l; j++) {
96
+
97
+ tableBody[0].appendChild(o[j]);
98
+
99
+ }
100
+ }
101
+
102
+ fixPosition(table,tableBody);
103
+
104
+ $(table).trigger("applyWidgets");
105
+
106
+ if( c.page >= c.totalPages ) {
107
+ moveToLastPage(table);
108
+ }
109
+
110
+ updatePageDisplay(c);
111
+ }
112
+
113
+ this.appender = function(table,rows) {
114
+
115
+ var c = table.config;
116
+
117
+ c.rowsCopy = rows;
118
+ c.totalRows = rows.length;
119
+ c.totalPages = Math.ceil(c.totalRows / c.size);
120
+ renderTable(table,rows);
121
+ };
122
+
123
+ this.defaults = {
124
+ size: 10,
125
+ offset: 0,
126
+ page: 0,
127
+ totalRows: 0,
128
+ totalPages: 0,
129
+ container: null,
130
+ cssNext: '.next',
131
+ cssPrev: '.prev',
132
+ cssFirst: '.first',
133
+ cssLast: '.last',
134
+ cssPageDisplay: '.pagedisplay',
135
+ cssPageSize: '.pagesize',
136
+ seperator: "/",
137
+ positionFixed: true,
138
+ appender: this.appender
139
+ };
140
+
141
+ this.construct = function(settings) {
142
+
143
+ return this.each(function() {
144
+
145
+ config = $.extend(this.config, $.tablesorterPager.defaults, settings);
146
+
147
+ var table = this, pager = config.container;
148
+
149
+ $(this).trigger("appendCache");
150
+
151
+ config.size = parseInt($(".pagesize",pager).val());
152
+
153
+ $(config.cssFirst,pager).click(function() {
154
+ moveToFirstPage(table);
155
+ return false;
156
+ });
157
+ $(config.cssNext,pager).click(function() {
158
+ moveToNextPage(table);
159
+ return false;
160
+ });
161
+ $(config.cssPrev,pager).click(function() {
162
+ moveToPrevPage(table);
163
+ return false;
164
+ });
165
+ $(config.cssLast,pager).click(function() {
166
+ moveToLastPage(table);
167
+ return false;
168
+ });
169
+ $(config.cssPageSize,pager).change(function() {
170
+ setPageSize(table,parseInt($(this).val()));
171
+ return false;
172
+ });
173
+ });
174
+ };
175
+
176
+ }
177
+ });
178
+ // extend plugin scope
179
+ $.fn.extend({
180
+ tablesorterPager: $.tablesorterPager.construct
181
+ });
182
+
183
+ })(jQuery);
@@ -0,0 +1,67 @@
1
+ #table_fu{
2
+ width: 980px;
3
+ margin-left: auto;
4
+ margin-right: auto;
5
+ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
6
+ font-size: 14px;
7
+ line-height: 20px;
8
+ color: #333;
9
+ }
10
+ #table_fu *{
11
+ margin: 0;
12
+ padding: 0;
13
+ }
14
+ #table_fu div#table_title{
15
+ font-size: 24px;
16
+ font-weight: bold;
17
+ font-family: Palatino Linotype,Book Antiqua,Palatino,serif;
18
+ }
19
+ #table_fu div{
20
+ margin-bottom: 14px;
21
+ }
22
+ #table_fu div#pager{
23
+ float:right;
24
+ width: 15%;
25
+ }
26
+ #table_fu div#pager div.pagedisplay{
27
+ display: inline;
28
+ }
29
+ #table_fu div#filter{
30
+ float:left;
31
+ width:30%;
32
+ }
33
+
34
+ #table_fu #data{
35
+ border: 1px solid #ababab;
36
+ border-collapse: collapse;
37
+ margin-bottom:14px;
38
+ width: 100%;
39
+ }
40
+ #table_fu td, #table_fu th{
41
+ padding: 7px;
42
+ border-bottom: 1px solid #ababab;
43
+ text-align: center;
44
+ }
45
+ #table_fu th{
46
+ cursor: pointer;
47
+ vertical-align: bottom;
48
+ }
49
+ #table_fu td.sorted{
50
+ background-color: #edf4ff;
51
+ }
52
+ #table_fu td .bar{
53
+ width: 100%;
54
+ height: 100%;
55
+ text-align:left;
56
+ background-color: #99CA3C;
57
+ color:#444;
58
+ padding:0.5em;
59
+ margin-top: 0.7em;
60
+ font-weight:bold;
61
+ }
62
+ #table_fu th.headerSortDown{
63
+ background: #ccc url(../images/th_arrow_asc.gif) no-repeat 100% 75%;
64
+ }
65
+ #table_fu th.headerSortUp{
66
+ background: #ccc url(../images/th_arrow_desc.gif) no-repeat 100% 75%;
67
+ }
data/index.html ADDED
@@ -0,0 +1,238 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
5
+ <title>TableSetter</title>
6
+ <link rel="stylesheet" type="text/css" href="documentation/css/styles.css" />
7
+ <link rel="stylesheet" type="text/css" href="documentation/css/dawn.css" />
8
+ </head>
9
+ <body>
10
+ <a href="http://www.propublica.org" class="propublica">&nbsp;</a>
11
+ <h1>TableSetter <small>&ndash; Version: 0.1.2</small></h1>
12
+ <p><a href="https://github.com/propublica/table-setter">TableSetter</a> is a Ruby app that provides an easy way to present CSVs hosted locally or remotely (e.g. on google, etc) in custom HTML. TableSetter in the wild: <a href="http://projects.propublica.org/tables/failed-banks">a list of all stimulus projects from last year</a>, <a href="http://projects.propublica.org/tables/stimulus-spending-progress">the stimulus spending progress</a>, or <a href="http://projects.propublica.org/tables/failed-banks">a list of failed banks due to the last recession</a>.</p>
13
+ <p>Each table is filterable and sortable on multiple columns. Also each column can be formatted in one of many different styles. In production mode, <strong>TableSetter</strong> provides valid expires headers and can be coupled with an upstream cache like <a href="http://rtomayko.github.com/rack-cache/">Rack::Cache</a> or varnish for speedy presentation.</p>
14
+ <h2><a id="toc">Table of Contents</a></h2>
15
+ <ul>
16
+ <li><a href="#installation">Installation</a></li>
17
+ <li><a href="#tablesetter">The table-setter command</a></li>
18
+ <li><a href="#thedirectory">The Configuration Directory</a></li>
19
+ <li><a href="#thefile">A TableSetter File</a></li>
20
+ <li><a href="#deployment">Deployment</a></li>
21
+ <li><a href="#rails">Rails</a></li>
22
+ <li><a href="#links">Links</a></li>
23
+ <li><a href="#credits">Credits</a></li>
24
+ <li><a href="#license">License</a></li>
25
+ </ul>
26
+ <h2><a id="installation" href="#toc">Installation</a></h2>
27
+ <p>Install <strong>TableSetter</strong> through rubygems:</p>
28
+ <pre class="dawn">
29
+ gem install table_setter</pre>
30
+ <p>or from the source files:</p>
31
+ <pre class="dawn">
32
+ git clone git://github.com/propublica/table-setter.git
33
+ cd table-setter
34
+ rake install</pre>
35
+ <p>After you've installed the gem you'll have a new executable: <strong>table-setter</strong>. You can view the subcommands available by typing <strong>table-setter --help</strong>. To set things up you'll need to run it with the <strong>install</strong> command to install the configuration files and ERB templates into a directory. </p>
36
+ <pre>
37
+ table-setter install path/to/directory</pre>
38
+ <p>
39
+ To start the development server run:
40
+ </p>
41
+ <pre>
42
+ table-setter start path/to/directory</pre>
43
+ <p>Go to development url, <a href="http://localhost:3000/">http://localhost:3000/</a> and you'll see a list of the example tables. You can peruse the examples <a href="http://propublica.github.com/table-setter/documentation/tables/">here</a>.</p>
44
+ <h2><a id="tablesetter" href="#toc">The table-setter command</a></h2>
45
+ <p>
46
+ The <strong>table-setter</strong> command responds to three subcommands:
47
+ <ul>
48
+ <li><strong>install &lt;DIRECTORY&gt;</strong> installs the tablesetter files into the current directory or one you specify.</li>
49
+ <li><strong>start &lt;DIRECTORY&gt;</strong> starts the development server so you can preview your tables before deploying.</li>
50
+ <li><strong>build &lt;DIRECTORY&gt; -p &lt;PATH&gt;</strong> builds a static version of the tables, <strong>&lt;PATH&gt;</strong> is the relative path where you want to place the tables on your webserver.
51
+ See <a href="#deployment">Deployment</a></li>
52
+ </ul>
53
+ </p>
54
+ <h2><a id="thedirectory" href="#toc">The Configuration Directory</a></h2>
55
+ <p>
56
+ The configuration folder contains the javascripts, stylesheets, view templates (written in <a href="http://www.ruby-doc.org/stdlib/libdoc/erb/rdoc/">ERB</a>), rackup file, and most importantly the configuration files for each table.
57
+ </p>
58
+ <p>You'll put table definition files in the <strong>table</strong> directory, your javascript in <strong>public/javascripts</strong> and css in <strong>public/stylesheets</strong>. You can make most HTML customizations in <strong>views/layout.erb</strong>.
59
+ <p class="file">
60
+ The <strong>config.ru</strong> file is a rackup file that instructs the webserver to start the <strong>TableSetter</strong> application and serve the assets contained in the configuration folder. In most cases you'll want to use apache and passenger (see <a href="#deployment">Deployment</a> for details).
61
+ </p>
62
+ <p class="dir">
63
+ In <strong>public</strong> you'll find the static assets required for the look and feel and functionality of the table:
64
+ </p>
65
+ <ul>
66
+ <li class="dir"> The <strong>images</strong> directory contains the small images <img src="template/public/images/th_arrow_asc.gif" style="display:inline"> and <img src="template/public/images/th_arrow_desc.gif" style="display:inline"> which show the user the sorting direction of a given column.</li>
67
+ <li class="dir"> The <strong>javascripts</strong> directory contains scripts that power the dynamic functionality of a table. Each file depends on <a href="http://jquery.com/">jQuery</a>:
68
+ <ul>
69
+ <li class="file"><strong>application.js</strong> dispatches to the other javascript files to render the table at load time. It also defines the column highlighting function.</li>
70
+ <li class="file"><strong>jquery.tablesorter.js</strong> the jQuery <a href="http://tablesorter.com/docs/">tablesorter plugin</a> that handles the dynamic sorting by column.</li>
71
+ <li class="file"><strong>jquery.tablesorter.pager.js</strong> the <a href="http://tablesorter.com/docs/example-pager.html">tablesorter pager plugin</a> to tablesorter that provides the paging functionality.</li>
72
+ <li class="file"><strong>jquery.tablesorter.multipagefilter.js</strong> a custom plugin that allows for searching across multiple pages</li>
73
+ </ul>
74
+ </li>
75
+ <li class="dir">The <strong>stylesheets</strong> directory contains <strong>application.css</strong>, the <strong>TableSetter</strong> stylesheet. Each style is prefaced with <strong>#tablefu</strong> so you should be able to include your organization's css on the page without affecting the tables.</li>
76
+ </ul>
77
+ <p class="dir">The <strong>tables</strong> directory contains yml configuration files for each of the tables you want to deploy. By default it contains:</p>
78
+ <ul>
79
+ <li class="file"><strong>example.yml</strong> contains most of the simple options in a <strong>TableSetter</strong> file.</li>
80
+ <li class="file"><strong>example_local.yml</strong> and <strong>example_local.csv</strong> shows how to build a table from a local file. It also is hard_paginated (see <a href="#thefile">A Table Setter File</a>).</li>
81
+ <li class="file"><strong>example_faceted.yml</strong> is an example of faceting.</li>
82
+ <li class="file"><strong>example_formatted.yml</strong> and <strong>example_formatted.csv</strong> shows how to apply formatting to a column and a group of columns.</li>
83
+ </ul>
84
+ <p class="dir">The <strong>views</strong> directory contains the <a href="http://www.ruby-doc.org/stdlib/libdoc/erb/rdoc/">ERB</a> templates needed to render the table pages and index page.</p>
85
+ <ul>
86
+ <li class="file">
87
+ <strong>404.erb</strong> and <strong>500.erb</strong> render when a table is not found or a server error occurs, respectively.
88
+ </li>
89
+ <li class="file"><strong>layout.erb</strong> contains the basic frame of the page. You should place most of your customizations here. If you'd like to add custom html, place it above or below the <strong>&lt;%=&nbsp;yield&nbsp;%&gt;</strong> tag.</li>
90
+ <li class="file"><strong>index.erb</strong> renders the table list for the root path of the app.</li>
91
+ <li class="file"><strong>table.erb</strong> renders an individual table. You shouldn't have to tweak this much, if at all.</li>
92
+ </ul>
93
+ <h2><a id="thefile" href="#toc">A TableSetter File</a></h2>
94
+ <p>Each TableSetter file is written in <a href="http://www.yaml.org/">YAML</a> and outlines the the display options for a particular table. The filename dictates the path where it will appear (e.g. a config file named example.yml will appear at <strong>http://host/example</strong>). Initially TableSetter installs a few examples to get you started (see <a href="#thedirectory">above</a>).</p>
95
+ <p>Each table setter file must begin with a <strong>table:</strong> declaration, and it's important to note that whitespace matters. For example consider this csv:</p>
96
+ <pre>
97
+ Bank,Spent,Funds,Link
98
+ McDuck Bank,100000,10000000,http://diveintomoney.com
99
+ Potter Savings and Loans,1100,1000000,http://angelsandwingsmrstewart.com
100
+ </pre>
101
+ <p>these are the example options in a <strong>TableSetter</strong> config file:
102
+ <pre>
103
+ table:
104
+ title: The title of the table
105
+
106
+ # google_key:, file:, or url: define how a table is loaded.
107
+ # only one is necessary
108
+ file: loads a local CSV file from the /tables directory.
109
+ url: will load a CSV file from an external server, and
110
+ google_key: is a google key url from an external google doc (see note).
111
+
112
+ deck: A HTML string describing the table, appears above the table itself.
113
+
114
+ footer: A HTML string for notes/caveats etc. Appears below the table.
115
+
116
+ column_options: # Defines a hash of options that are passed onto TableFu
117
+
118
+ columns: # A list of columns to include, for example:
119
+ - Bank # would only include the bank column in the table
120
+
121
+ style: # A list of style declarations by column, for example:
122
+ Bank: 'text-align:left;' # would left-align the text in the "Bank" column.
123
+
124
+ sorted_by: # Defines the sort order and column to sort by of the table.
125
+ Bank: ascending # would sort by the Bank column in ascending order
126
+
127
+ total: # Declares which columns should have a totals row.
128
+ ['Funds', 'Spent']
129
+
130
+ formatting: # Defines which of the TableFu formatters to apply to a column.
131
+ (%) Spent: bar # applies the bar formatter to the '(%) Spent' column.
132
+ Link: # Creates a meta column form two other columns
133
+ method: link # describes the formatter to use
134
+ arguments: ['Bank', 'URL'] # Combines Bank and URL as arguments
135
+
136
+ faceting: # Describes the faceting, or grouping, to apply to a table
137
+ facet_by: Bank # groups the records by bank name
138
+
139
+ hard_paginate: true # Dictates that the table should be spread across multiple pages
140
+ # can't be used with faceting, and disables the user filtering
141
+
142
+ per_page: 250 # Instructs TableSetter to only page by 250 rows.
143
+
144
+ live: true # publishes the table in the index page. Note that even if live is
145
+ # set to false the table is still accessible directly.
146
+
147
+ </pre>
148
+
149
+ <h3>NB: A Note About google_key</h3>
150
+ <p>At ProPublica, we mainly use TableSetter to format public google spreadsheets. You can find the <strong>google_key</strong> by publishing a spreadsheet as a webpage:</p>
151
+ <img src="documentation/images/publish.png">
152
+ <p>and in the dialog box, the google key is here:</p>
153
+ <img src="documentation/images/key.png">
154
+ <h2><a id="deployment" href="#toc">Deployment</a></h2>
155
+
156
+ <h3>Passenger</h3>
157
+ <em>(Cribbed from the excellent <a href="http://www.modrails.com/documentation/Users%20guide.html#_deploying_a_rack_based_ruby_application">passenger documentation</a>.)</em>
158
+ <p>If you're familiar with deploying a Rails application under passenger, not much changes when deploying a rack basked application. <strong>TableSetter</strong> includes a <strong>config.ru</strong> file that should be sufficient under most situations. You'll need to create a <strong>tmp</strong> directory inside the <strong>TableSetter</strong> directory on the server. The following virtual host configuration will deploy <strong>TableSetter</strong> directory:
159
+ </p>
160
+ <pre>
161
+ &lt;VirtualHost *:80&gt;
162
+ ServerName www.yourdomain.com
163
+ DocumentRoot /path/to/table-setter/public
164
+ &lt;/VirtualHost&gt;</pre>
165
+ <p>If you want to deploy <strong>TableSetter</strong> under a sub URI you should symlink the public folder to a directory in the document root:</p>
166
+ <pre>
167
+ ln -s /path/to/table-setter/public /docroot/tables</pre>
168
+ <p>
169
+ and change you're apache config to reflect the sub URI:
170
+ </p>
171
+ <pre>
172
+ &lt;VirtualHost *:80&gt;
173
+ ServerName www.yourdomain.com
174
+ DocumentRoot /docroot/tables
175
+ RackBaseURI /tables
176
+ &lt;/VirtualHost&gt;</pre>
177
+ <h3>Caching</h3>
178
+ <p>You probably don't want to parse a remote CSV file on every request in production, so the <strong>config.ru</strong> file contains directives to enable <strong>Rack:Cache</strong> a simple reverse proxy cache. If your web server is not behind an upstream cache you'll want to enable it by uncommenting the required lines.<p>
179
+ <p>You'll also want to enable the <strong>TableSetter:App</strong> expire time by uncommenting this line:</p>
180
+ <pre>
181
+ TableSetter::App.cache_timeout = 60 * 15 # 15 minutes</pre>
182
+ <p>That line dictates the max age of a request and you'll want to tweak it depending on how frequently changed your tables will be and how many users you expect. If you want to define any <a href="https://github.com/propublica/table-fu">TableFu</a> formatters you should do so in <strong>config.ru</strong>.</p>
183
+
184
+ <h3>Static</h3>
185
+ <p>You can also use to pre-build <strong>table-setter</strong> your tables as html and upload the built files to your web server. You can build them using the <strong>table-setter</strong> command:</p>
186
+ <pre>
187
+ table-setter build path/to/table-setter/directory -p path</pre>
188
+ <p>The build tables will be placed in the <strong>out</strong> directory inside the configuration directory.</p>
189
+ <p>The <strong>-p</strong> flag dictates where on the server relative to root you'll install the files. If I want my tables to appear under the <strong>tables/</strong> directory on my site, I'd run:</p>
190
+ <pre>
191
+ table-setter build path/to/table-setter/directory -p tables</pre>
192
+ <p>And upload the files in the <strong>out/tables</strong> directory.</p>
193
+ <h2><a id="rails" href="#toc">Rails</a></h2>
194
+ <p>In order to use table-setter as a Rails, you'll need to install the <a href="http://github.com/propublica/table-setter-generator">table-setter-generator </a> gem. Once you've done that you'll be able to run:</p>
195
+ <pre>
196
+ script/generate table-setter</pre>
197
+ <p>In your existing Rails app path and it will install the <strong>TableSetter</strong> routes, controller, views, and <strong>Table</strong> model.</p>
198
+ <h2><a id="links" href="#toc">Links</a></h2>
199
+ <ul>
200
+ <li><a href="http://github.com/propublica/table-fu">TableFu</a><br>
201
+ A gem that provides the conceptual backend to <strong>TableSetter</strong>.</li>
202
+ <li><a href="http://www.modrails.com/documentation/Users%20guide.html#_deploying_a_rack_based_ruby_application">Passenger Documentation</a><br>
203
+ The Passenger Documention section on how to deploy a rack app under passenger.</li>
204
+ <li><a href="http://docs.heroku.com/rack">Heroku Documentation</a><br>
205
+ How to deploy a rack app in heroku.</li>
206
+ <li><a href="http://github.com/propublica/table-fu/issues">Issues</a><br>
207
+ The github issue tracker. Post bug reports and feature requests here.</li>
208
+ <li><a href="http://www.yaml.org/">YAML</a>
209
+ The YAML homepage. <strong>TableSetter</strong> config files are YAML files.
210
+ </li>
211
+ <li><a href="doc/index.html">API Docs</a></li>
212
+ </ul>
213
+ <h2><a id="credits" href="#toc">Credits</a></h2>
214
+ <p><a href="http://github.com/thejefflarson">Jeff Larson</a> (Maintainer), <a href="http://github.com/brianboyer/">Brian Boyer</a>, <a href="http://github.com/kleinmatic">Scott Klein</a>, <a href="http://github.com/markpercival">Mark Percival</a>, and <a href="http://github.com/seebq">Charles Brian Quinn</a>.</p>
215
+ <h2><a id="license" href="#toc">License</a></h2>
216
+ <pre>Copyright (c) 2010 ProPublica
217
+
218
+ Permission is hereby granted, free of charge, to any person obtaining
219
+ a copy of this software and associated documentation files (the
220
+ "Software"), to deal in the Software without restriction, including
221
+ without limitation the rights to use, copy, modify, merge, publish,
222
+ distribute, sublicense, and/or sell copies of the Software, and to
223
+ permit persons to whom the Software is furnished to do so, subject to
224
+ the following conditions:
225
+
226
+ The above copyright notice and this permission notice shall be
227
+ included in all copies or substantial portions of the Software.
228
+
229
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
230
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
231
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
232
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
233
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
234
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
235
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
236
+ </pre>
237
+ </body>
238
+ </html>
@@ -0,0 +1,54 @@
1
+ require 'sinatra/static_assets'
2
+ require 'sinatra/url_for'
3
+
4
+ module TableSetter
5
+ class App < Sinatra::Base
6
+ helpers Sinatra::UrlForHelper
7
+ register Sinatra::StaticAssets
8
+ set :root, TableSetter.config_path
9
+ # serve static files from the public directory
10
+ enable :static
11
+
12
+ not_found do
13
+ show :"404"
14
+ end
15
+
16
+ error do
17
+ show :"500"
18
+ end
19
+
20
+ get "/" do
21
+ headers['Cache-Control'] = "public, max-age=#{TableSetter::App.cache_timeout}"
22
+ last_modified Table.fresh_yaml_time
23
+ show :index, :tables => Table.all
24
+ end
25
+
26
+ ["/:slug/:page/", "/:slug/"].each do |path|
27
+ get path do
28
+ headers['Cache-Control'] = "public, max-age=#{TableSetter::App.cache_timeout}"
29
+ not_found unless Table.exists? params[:slug]
30
+ table = Table.new(params[:slug], :defer => true)
31
+ last_modified table.updated_at
32
+ table.load
33
+ page = params[:page] || 1
34
+ table.paginate! page
35
+ show :table, :table => table, :page => page
36
+ end
37
+ end
38
+
39
+ private
40
+
41
+ def show(page, locals={})
42
+ erb page, {:layout => true}, locals
43
+ end
44
+
45
+ class << self
46
+ attr_accessor :cache_timeout
47
+
48
+ def cache_timeout
49
+ @cache_timeout || 0
50
+ end
51
+
52
+ end
53
+ end
54
+ end