table_fu 0.3.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -1,3 +1,5 @@
1
+ $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__))
2
+
1
3
  require 'rubygems'
2
4
  require 'rake'
3
5
 
@@ -37,6 +39,7 @@ task :default => :spec
37
39
  require 'rake/rdoctask'
38
40
  Rake::RDocTask.new do |rdoc|
39
41
  if File.exist?('VERSION.yml')
42
+ require 'yaml'
40
43
  config = YAML.load(File.read('VERSION.yml'))
41
44
  version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
42
45
  else
@@ -50,7 +53,7 @@ Rake::RDocTask.new do |rdoc|
50
53
  end
51
54
 
52
55
  desc "render documentation for gh-pages"
53
- task :gh do
56
+ task :gh do
54
57
  require 'erb'
55
58
  File.open("index.html", "w") do |f|
56
59
  f.write ERB.new(File.open("documentation/index.html.erb").read).result
@@ -1,5 +1,5 @@
1
1
  ---
2
- :patch: 0
2
+ :minor: 3
3
+ :patch: 1
3
4
  :build:
4
5
  :major: 0
5
- :minor: 3
@@ -1,8 +1,7 @@
1
1
  <%
2
2
  $:.unshift File.expand_path(File.dirname(__FILE__), "/../lib/table_fu")
3
-
3
+ require 'rubygems'
4
4
  require 'uv'
5
- require 'FasterCSV'
6
5
  require 'table_fu'
7
6
 
8
7
  def code_for(file, output=true)
@@ -35,10 +34,10 @@ end %>
35
34
  <link rel="stylesheet" type="text/css" href="documentation/css/styles.css" />
36
35
  <link rel="stylesheet" type="text/css" href="documentation/css/dawn.css" />
37
36
  </head>
38
-
37
+
39
38
  <body>
40
39
  <a href="http://www.propublica.org" class="propublica">&nbsp;</a>
41
- <h1>TableFu <small>&ndash; Version: <%=
40
+ <h1>TableFu <small>&ndash; Version: <%=
42
41
  config = YAML.load(File.read('VERSION.yml'))
43
42
  "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
44
43
  %></small></h1>
@@ -85,10 +84,10 @@ end %>
85
84
  Note that the columns are still accessible directly even if they're not in the columns array.
86
85
  <%= code_for "columns_hidden" %>
87
86
  </p>
88
-
87
+
89
88
  <h2><a id="macros" href="#toc">Macros / Formatting</a></h2>
90
89
  <p><strong>TableFu</strong> allows you to format columns of cells through the use of macros. See <a href="http://github.com/propublica/table-fu/blob/master/lib/table_fu/formatting.rb"><strong>TableFu::Formatting</strong></a> for the predefined macros available. </p>
91
- <p>The <strong>formatting</strong> attribute should be a hash of the form:
90
+ <p>The <strong>formatting</strong> attribute should be a hash of the form:
92
91
  <%= code_for "formatting_options", false %>
93
92
  which will call the macro on the column name with the arguments if specified.
94
93
  </p>
@@ -99,7 +98,7 @@ end %>
99
98
  <p>Of course, you can provide your own macros by patching <a href="http://github.com/propublica/table-fu/blob/master/lib/table_fu/formatting.rb">TableFu::Formatting</a>. <a href="http://www.github.com/propublica/table-setter">TableSetter</a> includes rails view helpers directly in <strong>TableFu::Formatting</strong>.
100
99
  <%= code_for "rails_helpers", false %>
101
100
  </p>
102
-
101
+
103
102
  <h2><a id="faceting" href="#toc">Faceting</a></h2>
104
103
  <p>Faceting provides a way to group rows together using a cell value they share in common. Calling <strong>TableFu#faceted_by</strong> returns an array of table fu instances each with a <strong>faceted_on</strong> attribute and with only the rows where that value appears.
105
104
  </p>
data/index.html CHANGED
@@ -0,0 +1,196 @@
1
+
2
+ <!DOCTYPE html>
3
+ <html>
4
+ <head>
5
+ <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
6
+ <title>TableFu</title>
7
+ <link rel="stylesheet" type="text/css" href="documentation/css/styles.css" />
8
+ <link rel="stylesheet" type="text/css" href="documentation/css/dawn.css" />
9
+ </head>
10
+
11
+ <body>
12
+ <a href="http://www.propublica.org" class="propublica">&nbsp;</a>
13
+ <h1>TableFu <small>&ndash; Version: 0.3.1</small></h1>
14
+
15
+ <p><a href="http://github.com/propublica/table-fu">TableFu</a> is a ruby gem for spreadsheet-style handling of arrays (e.g. filtering, formatting, and sorting by "column" or "row"). In addition, it has the ability to <a href="#facet">facet</a> &mdash; or group &mdash; rows according to cell value. It was developed as a backend for its companion project <a href="http://www.github.com/propublica/table-setter">TableSetter</a>.</p>
16
+ <p>For example, <strong>TableFu</strong> can consume a csv file and sort on a column:
17
+ <pre class="dawn">csv <span class="Keyword">=</span>&lt;&lt;-CSV
18
+ Author,Best Book,Number of Pages,Style
19
+ Samuel Beckett,Malone Muert,120,Modernism
20
+ James Joyce,Ulysses,644,Modernism
21
+ Nicholson Baker,Mezannine,150,Minimalism
22
+ Vladimir Sorokin,The Queue,263,Satire
23
+ CSV
24
+
25
+ spreadsheet <span class="Keyword">=</span> <span class="Support">TableFu</span><span class="PunctuationSeparator">.</span><span class="Entity">new</span>(csv) <span class="Keyword">do </span><span class="PunctuationSeparator">|</span><span class="Variable">s</span><span class="PunctuationSeparator">|</span>
26
+ s<span class="PunctuationSeparator">.</span><span class="Entity">sorted_by</span> <span class="Keyword">=</span> {'Best Book' <span class="PunctuationSeparator">=&gt;</span> {'order' <span class="PunctuationSeparator">=&gt;</span> 'ascending'}}
27
+ <span class="Keyword">end</span>
28
+ </pre><em>Returns:</em><pre class="dawn">[[&quot;Samuel Beckett&quot;<span class="PunctuationSeparator">,</span> &quot;Malone Muert&quot;<span class="PunctuationSeparator">,</span> &quot;120&quot;<span class="PunctuationSeparator">,</span> &quot;Modernism&quot;]<span class="PunctuationSeparator">,</span> [&quot;Nicholson Baker&quot;<span class="PunctuationSeparator">,</span> &quot;Mezannine&quot;<span class="PunctuationSeparator">,</span> &quot;150&quot;<span class="PunctuationSeparator">,</span> &quot;Minimalism&quot;]<span class="PunctuationSeparator">,</span> [&quot;Vladimir Sorokin&quot;<span class="PunctuationSeparator">,</span> &quot;The Queue&quot;<span class="PunctuationSeparator">,</span> &quot;263&quot;<span class="PunctuationSeparator">,</span> &quot;Satire&quot;]<span class="PunctuationSeparator">,</span> [&quot;James Joyce&quot;<span class="PunctuationSeparator">,</span> &quot;Ulysses&quot;<span class="PunctuationSeparator">,</span> &quot;644&quot;<span class="PunctuationSeparator">,</span> &quot;Modernism&quot;]]
29
+ </pre>
30
+ </p>
31
+ <h2><a id="toc">Table of Contents</a></h2>
32
+ <ul>
33
+ <li><a href="#installation">Installation</a></li>
34
+ <li><a href="#usage">Usage</a></li>
35
+ <li><a href="#macros">Macros / Formatting</a></li>
36
+ <li><a href="#faceting">Faceting</a></li>
37
+ <li><a href="#manipulation">Manipulation</a></li>
38
+ <li><a href="#links">Links</a></li>
39
+ <li><a href="#credits">Credits</a></li>
40
+ <li><a href="#license">License</a></li>
41
+ </ul>
42
+ <h2><a id="installation" href="#toc">Installation</a></h2>
43
+ <p><strong>TableFu</strong> is available as a rubygem:
44
+ <pre class="dawn">
45
+ gem install table_fu</pre>
46
+ or from the actual source:
47
+ <pre class="dawn">
48
+ git clone git://github.com/propublica/table-fu.git
49
+ cd table-fu
50
+ rake install</pre>
51
+ </p>
52
+ <h2><a id="usage" href="#toc">Usage</a></h2>
53
+ <p>
54
+ The <strong>TableFu</strong> constructor takes two arguments; a 2 dimensional array or csv (file object or string) and a hash of column options or a block. TableFu will assume that the first row of the array contains the column headers. The simple options are:</p>
55
+ <p><strong>sorted_by:</strong> the column to sort by, it defaults to no sorting at all.
56
+ <pre class="dawn">csv <span class="Keyword">=</span>&lt;&lt;-CSV
57
+ Author,Best Book,Number of Pages,Style
58
+ Samuel Beckett,Malone Muert,120,Modernism
59
+ James Joyce,Ulysses,644,Modernism
60
+ Nicholson Baker,Mezannine,150,Minimalism
61
+ Vladimir Sorokin,The Queue,263,Satire
62
+ CSV
63
+
64
+ spreadsheet <span class="Keyword">=</span> <span class="Support">TableFu</span><span class="PunctuationSeparator">.</span><span class="Entity">new</span>(csv) <span class="Keyword">do </span><span class="PunctuationSeparator">|</span><span class="Variable">s</span><span class="PunctuationSeparator">|</span>
65
+ s<span class="PunctuationSeparator">.</span><span class="Entity">sorted_by</span> <span class="Keyword">=</span> {'Best Book' <span class="PunctuationSeparator">=&gt;</span> {'order' <span class="PunctuationSeparator">=&gt;</span> 'ascending'}}
66
+ <span class="Keyword">end</span>
67
+ </pre><em>Returns:</em><pre class="dawn">[[&quot;Samuel Beckett&quot;<span class="PunctuationSeparator">,</span> &quot;Malone Muert&quot;<span class="PunctuationSeparator">,</span> &quot;120&quot;<span class="PunctuationSeparator">,</span> &quot;Modernism&quot;]<span class="PunctuationSeparator">,</span> [&quot;Nicholson Baker&quot;<span class="PunctuationSeparator">,</span> &quot;Mezannine&quot;<span class="PunctuationSeparator">,</span> &quot;150&quot;<span class="PunctuationSeparator">,</span> &quot;Minimalism&quot;]<span class="PunctuationSeparator">,</span> [&quot;Vladimir Sorokin&quot;<span class="PunctuationSeparator">,</span> &quot;The Queue&quot;<span class="PunctuationSeparator">,</span> &quot;263&quot;<span class="PunctuationSeparator">,</span> &quot;Satire&quot;]<span class="PunctuationSeparator">,</span> [&quot;James Joyce&quot;<span class="PunctuationSeparator">,</span> &quot;Ulysses&quot;<span class="PunctuationSeparator">,</span> &quot;644&quot;<span class="PunctuationSeparator">,</span> &quot;Modernism&quot;]]
68
+ </pre>
69
+ </p>
70
+ <p><strong>columns:</strong> the columns to include in the table, useful when reordering and filtering extraneous columns. If no arguments are provided, <strong>TableFu</strong> will include all columns by default.
71
+ <pre class="dawn">spreadsheet <span class="Keyword">=</span> <span class="Support">TableFu</span><span class="PunctuationSeparator">.</span><span class="Entity">new</span>(csv) <span class="Keyword">do </span><span class="PunctuationSeparator">|</span><span class="Variable">s</span><span class="PunctuationSeparator">|</span>
72
+ s<span class="PunctuationSeparator">.</span><span class="Entity">columns</span> <span class="Keyword">=</span> [&quot;Best Book&quot;<span class="PunctuationSeparator">,</span> &quot;Author&quot;]
73
+ <span class="Keyword">end</span>
74
+
75
+ spreadsheet<span class="PunctuationSeparator">.</span><span class="Entity">columns</span><span class="PunctuationSeparator">.</span><span class="Entity">map</span> <span class="Keyword">do </span><span class="PunctuationSeparator">|</span><span class="Variable">column</span><span class="PunctuationSeparator">|</span>
76
+ spreadsheet<span class="PunctuationSeparator">.</span><span class="Entity">rows</span>[<span class="Constant">0</span>]<span class="PunctuationSeparator">.</span><span class="Entity">column_for</span>(column)<span class="PunctuationSeparator">.</span><span class="Entity">to_s</span>
77
+ <span class="Keyword">end</span>
78
+ </pre><em>Returns:</em><pre class="dawn">[&quot;Malone Muert&quot;<span class="PunctuationSeparator">,</span> &quot;Samuel Beckett&quot;]
79
+ </pre>
80
+ Note that the columns are still accessible directly even if they're not in the columns array.
81
+ <pre class="dawn">spreadsheet <span class="Keyword">=</span> <span class="Support">TableFu</span><span class="PunctuationSeparator">.</span><span class="Entity">new</span>(csv) <span class="Keyword">do </span><span class="PunctuationSeparator">|</span><span class="Variable">s</span><span class="PunctuationSeparator">|</span>
82
+ s<span class="PunctuationSeparator">.</span><span class="Entity">columns</span> <span class="Keyword">=</span> [&quot;Best Book&quot;<span class="PunctuationSeparator">,</span> &quot;Author&quot;]
83
+ <span class="Keyword">end</span>
84
+
85
+ spreadsheet<span class="PunctuationSeparator">.</span><span class="Entity">rows</span>[<span class="Constant">0</span>]['Style']<span class="PunctuationSeparator">.</span>to_s
86
+ </pre><em>Returns:</em><pre class="dawn">&quot;Modernism&quot;
87
+ </pre>
88
+ </p>
89
+
90
+ <h2><a id="macros" href="#toc">Macros / Formatting</a></h2>
91
+ <p><strong>TableFu</strong> allows you to format columns of cells through the use of macros. See <a href="http://github.com/propublica/table-fu/blob/master/lib/table_fu/formatting.rb"><strong>TableFu::Formatting</strong></a> for the predefined macros available. </p>
92
+ <p>The <strong>formatting</strong> attribute should be a hash of the form:
93
+ <pre class="dawn">{&quot;Column Name&quot; <span class="PunctuationSeparator">=&gt;</span> 'Formatting Method Name'}
94
+
95
+ <span class="Comment"><span class="Comment">#</span> or</span>
96
+
97
+ {&quot;Meta Column Name&quot; <span class="PunctuationSeparator">=&gt;</span> {&quot;method&quot; <span class="PunctuationSeparator">=&gt;</span> &quot;Method Name&quot;<span class="PunctuationSeparator">,</span> &quot;arguments&quot; <span class="PunctuationSeparator">=&gt;</span> ['Column 1'<span class="PunctuationSeparator">,</span> 'Column 2' <span class="PunctuationSeparator">.</span><span class="PunctuationSeparator">.</span><span class="PunctuationSeparator">.</span> 'Column N']}}
98
+
99
+ </pre>
100
+ which will call the macro on the column name with the arguments if specified.
101
+ </p>
102
+ <p>For example, you can use the <strong>last_name</strong> formatter to extract the last name of a cell containing a person's name:
103
+ <pre class="dawn">spreadsheet <span class="Keyword">=</span> <span class="Support">TableFu</span><span class="PunctuationSeparator">.</span><span class="Entity">new</span>(csv) <span class="Keyword">do </span><span class="PunctuationSeparator">|</span><span class="Variable">s</span><span class="PunctuationSeparator">|</span>
104
+ s<span class="PunctuationSeparator">.</span><span class="Entity">formatting</span> <span class="Keyword">=</span> {&quot;Author&quot; <span class="PunctuationSeparator">=&gt;</span> 'last_name'}
105
+ <span class="Keyword">end</span>
106
+ spreadsheet<span class="PunctuationSeparator">.</span><span class="Entity">rows</span>[<span class="Constant">0</span>]<span class="PunctuationSeparator">.</span><span class="Entity">column_for</span>('Author')<span class="PunctuationSeparator">.</span>to_s
107
+ </pre><em>Returns:</em><pre class="dawn">&quot;Beckett&quot;
108
+ </pre>
109
+ </p>
110
+
111
+ <p>Of course, you can provide your own macros by patching <a href="http://github.com/propublica/table-fu/blob/master/lib/table_fu/formatting.rb">TableFu::Formatting</a>. <a href="http://www.github.com/propublica/table-setter">TableSetter</a> includes rails view helpers directly in <strong>TableFu::Formatting</strong>.
112
+ <pre class="dawn"><span class="Keyword">class</span> <span class="Entity">TableFu::Formatting</span>
113
+ <span class="Keyword">extend</span> <span class="Support">ActionView</span><span class="PunctuationSeparator">::</span><span class="Entity">Helpers</span><span class="PunctuationSeparator">::</span><span class="Entity">NumberHelper</span>
114
+ <span class="Keyword">end</span>
115
+ </pre>
116
+ </p>
117
+
118
+ <h2><a id="faceting" href="#toc">Faceting</a></h2>
119
+ <p>Faceting provides a way to group rows together using a cell value they share in common. Calling <strong>TableFu#faceted_by</strong> returns an array of table fu instances each with a <strong>faceted_on</strong> attribute and with only the rows where that value appears.
120
+ </p>
121
+ <p>In this example there are 2 rows where "Modernism" appears in the style column, so calling <strong>faceted_on</strong> with the argument <strong>"Style"</strong> returns a <strong>TableFu</strong> instance with those rows grouped together:
122
+ <pre class="dawn">spreadsheet <span class="Keyword">=</span> <span class="Support">TableFu</span><span class="PunctuationSeparator">.</span><span class="Entity">new</span>(csv)
123
+ spreadsheet<span class="PunctuationSeparator">.</span><span class="Entity">faceted_by</span> &quot;Style&quot;
124
+ </pre><em>Returns:</em><pre class="dawn">table<span class="PunctuationSeparator">.</span><span class="Entity">faceted_on</span> <span class="PunctuationSeparator">=&gt;</span> <span class="Variable">Minimalism</span><span class="PunctuationSeparator">,</span> table<span class="PunctuationSeparator">.</span><span class="Entity">rows</span> <span class="PunctuationSeparator">=&gt;</span> [[&quot;Nicholson Baker&quot;<span class="PunctuationSeparator">,</span> &quot;Mezannine&quot;<span class="PunctuationSeparator">,</span> &quot;150&quot;<span class="PunctuationSeparator">,</span> &quot;Minimalism&quot;]]
125
+ table<span class="PunctuationSeparator">.</span><span class="Entity">faceted_on</span> <span class="PunctuationSeparator">=&gt;</span> <span class="Variable">Modernism</span><span class="PunctuationSeparator">,</span> table<span class="PunctuationSeparator">.</span><span class="Entity">rows</span> <span class="PunctuationSeparator">=&gt;</span> [[&quot;Samuel Beckett&quot;<span class="PunctuationSeparator">,</span> &quot;Malone Muert&quot;<span class="PunctuationSeparator">,</span> &quot;120&quot;<span class="PunctuationSeparator">,</span> &quot;Modernism&quot;]<span class="PunctuationSeparator">,</span> [&quot;James Joyce&quot;<span class="PunctuationSeparator">,</span> &quot;Ulysses&quot;<span class="PunctuationSeparator">,</span> &quot;644&quot;<span class="PunctuationSeparator">,</span> &quot;Modernism&quot;]]
126
+ table<span class="PunctuationSeparator">.</span><span class="Entity">faceted_on</span> <span class="PunctuationSeparator">=&gt;</span> <span class="Variable">Satire</span><span class="PunctuationSeparator">,</span> table<span class="PunctuationSeparator">.</span><span class="Entity">rows</span> <span class="PunctuationSeparator">=&gt;</span> [[&quot;Vladimir Sorokin&quot;<span class="PunctuationSeparator">,</span> &quot;The Queue&quot;<span class="PunctuationSeparator">,</span> &quot;263&quot;<span class="PunctuationSeparator">,</span> &quot;Satire&quot;]]
127
+ </pre>
128
+ </p>
129
+ <h2><a id="manipulation" href="#toc">Manipulation / Output</a></h2>
130
+ <h3>Deleting Rows</h3>
131
+ <p>In addition to hiding columns and faceting <strong>TableFu</strong> can delete rows from the csv. Let's get rid of James Joyce (no one ever finished <em>Ulysses</em> anyway):
132
+ <pre class="dawn">spreadsheet <span class="Keyword">=</span> <span class="Support">TableFu</span><span class="PunctuationSeparator">.</span><span class="Entity">new</span>(csv) <span class="Keyword">do </span><span class="PunctuationSeparator">|</span><span class="Variable">s</span><span class="PunctuationSeparator">|</span>
133
+ s<span class="PunctuationSeparator">.</span><span class="Entity">delete_rows!</span> [<span class="Constant">1</span>]
134
+ <span class="Keyword">end</span>
135
+ </pre><em>Returns:</em><pre class="dawn">[[&quot;Samuel Beckett&quot;<span class="PunctuationSeparator">,</span> &quot;Malone Muert&quot;<span class="PunctuationSeparator">,</span> &quot;120&quot;<span class="PunctuationSeparator">,</span> &quot;Modernism&quot;]<span class="PunctuationSeparator">,</span> [&quot;Nicholson Baker&quot;<span class="PunctuationSeparator">,</span> &quot;Mezannine&quot;<span class="PunctuationSeparator">,</span> &quot;150&quot;<span class="PunctuationSeparator">,</span> &quot;Minimalism&quot;]<span class="PunctuationSeparator">,</span> [&quot;Vladimir Sorokin&quot;<span class="PunctuationSeparator">,</span> &quot;The Queue&quot;<span class="PunctuationSeparator">,</span> &quot;263&quot;<span class="PunctuationSeparator">,</span> &quot;Satire&quot;]]
136
+ </pre>
137
+ The deleted rows are still available in <strong>@deleted_rows</strong> for later access:
138
+ <pre class="dawn">spreadsheet <span class="Keyword">=</span> <span class="Support">TableFu</span><span class="PunctuationSeparator">.</span><span class="Entity">new</span>(csv) <span class="Keyword">do </span><span class="PunctuationSeparator">|</span><span class="Variable">s</span><span class="PunctuationSeparator">|</span>
139
+ s<span class="PunctuationSeparator">.</span><span class="Entity">delete_rows!</span> [<span class="Constant">1</span>]
140
+ <span class="Keyword">end</span>
141
+ </pre><em>Returns:</em><pre class="dawn">table<span class="PunctuationSeparator">.</span><span class="Entity">deleted_rows</span> <span class="PunctuationSeparator">=&gt;</span> [[&quot;James Joyce&quot;<span class="PunctuationSeparator">,</span> &quot;Ulysses&quot;<span class="PunctuationSeparator">,</span> &quot;644&quot;<span class="PunctuationSeparator">,</span> &quot;Modernism&quot;]]
142
+ </pre>
143
+ </p>
144
+ <h3>Pagination</h3>
145
+ <p>If you want only a chunk of the data, say to paginate your table, you can call <strong>only!</strong> with the range of values you want to keep:
146
+ <pre class="dawn">spreadsheet <span class="Keyword">=</span> <span class="Support">TableFu</span><span class="PunctuationSeparator">.</span><span class="Entity">new</span>(csv) <span class="Keyword">do </span><span class="PunctuationSeparator">|</span><span class="Variable">s</span><span class="PunctuationSeparator">|</span>
147
+ s<span class="PunctuationSeparator">.</span><span class="Entity">sorted_by</span> <span class="Keyword">=</span> {'Style' <span class="PunctuationSeparator">=&gt;</span> {&quot;order&quot; <span class="PunctuationSeparator">=&gt;</span> 'ascending'}}
148
+ <span class="Keyword">end</span>
149
+
150
+ spreadsheet<span class="PunctuationSeparator">.</span><span class="Entity">only!</span>(<span class="Constant">2</span><span class="PunctuationSeparator">.</span><span class="PunctuationSeparator">.</span><span class="Constant">4</span>)
151
+ spreadsheet<span class="PunctuationSeparator">.</span>rows
152
+ </pre><em>Returns:</em><pre class="dawn">[[&quot;Nicholson Baker&quot;<span class="PunctuationSeparator">,</span> &quot;Mezannine&quot;<span class="PunctuationSeparator">,</span> &quot;150&quot;<span class="PunctuationSeparator">,</span> &quot;Minimalism&quot;]<span class="PunctuationSeparator">,</span> [&quot;Vladimir Sorokin&quot;<span class="PunctuationSeparator">,</span> &quot;The Queue&quot;<span class="PunctuationSeparator">,</span> &quot;263&quot;<span class="PunctuationSeparator">,</span> &quot;Satire&quot;]]
153
+ </pre>
154
+ </p>
155
+ <h3>Sum</h3>
156
+ <p>TableFu can also sum a column of values:
157
+ <pre class="dawn">spreadsheet <span class="Keyword">=</span> <span class="Support">TableFu</span><span class="PunctuationSeparator">.</span><span class="Entity">new</span>(csv)
158
+ spreadsheet<span class="PunctuationSeparator">.</span><span class="Entity">total_for</span>('Number of Pages')<span class="PunctuationSeparator">.</span>to_s
159
+ </pre><em>Returns:</em><pre class="dawn"><span class="Constant">1177</span>
160
+ </pre>
161
+ </p>
162
+ <h2><a id="links" href="#toc">Links</a></h2>
163
+ <ul>
164
+ <li><a href="http://github.com/propublica/table-setter">TableSetter</a><br>A Sinatra app that uses <strong>TableFu</strong> to render public Google Spreadsheets in custom HTML.</li>
165
+ <li><a href="http://github.com/propublica/table-setter-generator">TableSetter Generator</a><br>
166
+ A rails generator that allows you to create a rails app like TableSetter.
167
+ </li>
168
+ <li><a href="http://github.com/propublica/table-fu/issues">Issues</a><br>Post bug reports and feature requests here.</li>
169
+ <li><a href="doc/index.html">API Docs</a></li>
170
+ </ul>
171
+ <h2><a id="credits" href="#toc">Credits</a></h2>
172
+ <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>
173
+ <h2><a id="license" href="#toc">License</a></h2>
174
+ <pre>Copyright (c) 2010 ProPublica
175
+
176
+ Permission is hereby granted, free of charge, to any person obtaining
177
+ a copy of this software and associated documentation files (the
178
+ "Software"), to deal in the Software without restriction, including
179
+ without limitation the rights to use, copy, modify, merge, publish,
180
+ distribute, sublicense, and/or sell copies of the Software, and to
181
+ permit persons to whom the Software is furnished to do so, subject to
182
+ the following conditions:
183
+
184
+ The above copyright notice and this permission notice shall be
185
+ included in all copies or substantial portions of the Software.
186
+
187
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
188
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
189
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
190
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
191
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
192
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
193
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
194
+ </pre>
195
+ </body>
196
+ </html>
@@ -1,5 +1,9 @@
1
- require 'rubygems'
2
- require 'fastercsv'
1
+ if RUBY_VERSION > "1.9"
2
+ require 'csv'
3
+ ::FasterCSV = CSV
4
+ else
5
+ require 'fastercsv'
6
+ end
3
7
 
4
8
 
5
9
  # TableFu turns a matric array(from a csv file for example) into a spreadsheet.
@@ -9,10 +13,10 @@ require 'fastercsv'
9
13
  # Documentation:
10
14
  # http://propublica.github.com/table-fu
11
15
  class TableFu
12
-
16
+
13
17
  attr_reader :deleted_rows, :table, :totals, :column_headers
14
18
  attr_accessor :faceted_on, :col_opts
15
-
19
+
16
20
  # Should be initialized with a matrix array or a string containing a csv, and expects the first
17
21
  # array in the matrix to be column headers.
18
22
  def initialize(table, column_opts = {})
@@ -28,11 +32,11 @@ class TableFu
28
32
  end
29
33
 
30
34
 
31
-
35
+
32
36
  # Pass it an array and it will delete it from the table, but save the data in
33
37
  # @deleted_rows@ for later perusal.
34
- #
35
- # Returns:
38
+ #
39
+ # Returns:
36
40
  # nothing
37
41
  def delete_rows!(arr)
38
42
  @deleted_rows ||= []
@@ -42,8 +46,8 @@ class TableFu
42
46
  end
43
47
  @table.compact!
44
48
  end
45
-
46
-
49
+
50
+
47
51
  # Inverse slice: Only keep the rows in the range after sorting
48
52
  def only!(range)
49
53
  rows_to_exclude = rows.map do |row|
@@ -56,7 +60,7 @@ class TableFu
56
60
  def row_at(row_num)
57
61
  TableFu::Row.new(@table[row_num], row_num, self)
58
62
  end
59
-
63
+
60
64
  # Returns all the Row objects for this object as a collection
61
65
  def rows
62
66
  all_rows = []
@@ -65,13 +69,13 @@ class TableFu
65
69
  end
66
70
  all_rows.sort
67
71
  end
68
-
72
+
69
73
  # Return the headers defined in column headers or cherry picked from @col_opts
70
74
  def columns
71
75
  @col_opts[:columns] || column_headers
72
76
  end
73
-
74
- # Return the headers of the array
77
+
78
+ # Return the headers of the array
75
79
  def headers
76
80
  all_columns = []
77
81
  columns.each do |header|
@@ -79,18 +83,18 @@ class TableFu
79
83
  end
80
84
  all_columns
81
85
  end
82
-
86
+
83
87
  # Sum the values of a particular column
84
88
  def sum_totals_for(column)
85
89
  @totals[column.to_s] = rows.inject(0) { |sum, r| to_numeric(r.datum_for(column).value) + sum }
86
90
  end
87
-
91
+
88
92
  # Sum the values of a particular column and return a Datum
89
93
  def total_for(column)
90
94
  sum_totals_for(column)
91
95
  Datum.new(@totals[column.to_s], column, nil, self)
92
96
  end
93
-
97
+
94
98
  # Return an array of TableFu instances grouped by a column.
95
99
  def faceted_by(column, opts = {})
96
100
  faceted_spreadsheets = {}
@@ -100,7 +104,7 @@ class TableFu
100
104
  faceted_spreadsheets[row.column_for(column).value] << row
101
105
  end
102
106
  end
103
-
107
+
104
108
  # Create new table_fu instances for each facet
105
109
  tables = []
106
110
  faceted_spreadsheets.each do |key,value|
@@ -110,11 +114,11 @@ class TableFu
110
114
  table.col_opts = @col_opts #formatting should be carried through
111
115
  tables << table
112
116
  end
113
-
117
+
114
118
  tables.sort! do |a,b|
115
119
  a.faceted_on <=> b.faceted_on
116
120
  end
117
-
121
+
118
122
  if opts[:total]
119
123
  opts[:total].each do |c|
120
124
  tables.each do |f|
@@ -122,10 +126,10 @@ class TableFu
122
126
  end
123
127
  end
124
128
  end
125
-
129
+
126
130
  tables
127
131
  end
128
-
132
+
129
133
  # Return a numeric instance for a string number, or if it's a string we
130
134
  # return 1, this way if we total up a series of strings it's a count
131
135
  def to_numeric(num)
@@ -137,70 +141,70 @@ class TableFu
137
141
  1 # We count each instance of a string this way
138
142
  end
139
143
  end
140
-
144
+
141
145
  # Return true if this table is faceted
142
146
  def faceted?
143
147
  not faceted_on.nil?
144
148
  end
145
-
149
+
146
150
  # Return the sorted_by column
147
151
  def sorted_by
148
152
  @col_opts[:sorted_by]
149
153
  end
150
-
154
+
151
155
  # Set the sorted_by column
152
156
  def sorted_by=(header)
153
157
  @col_opts[:sorted_by] = header
154
158
  end
155
-
159
+
156
160
  # Return the formatting hash
157
161
  def formatting
158
162
  @col_opts[:formatting]
159
163
  end
160
-
164
+
161
165
  # Set the formatting hash
162
166
  def formatting=(headers)
163
167
  @col_opts[:formatting] = headers
164
168
  end
165
-
169
+
166
170
  # Set up the cherry picked columns
167
171
  def columns=(array)
168
172
  @col_opts[:columns] = array
169
173
  end
170
-
171
174
 
172
-
175
+
176
+
173
177
  end
174
178
 
175
179
  class TableFu
176
180
  # TableFu::Row adds functionality to an row array in a TableFu instance
177
181
  class Row < Array
178
-
182
+
179
183
  attr_reader :row_num
180
-
184
+
181
185
  def initialize(row, row_num, spreadsheet)
182
186
  self.replace row
183
187
  @row_num = row_num
184
188
  @spreadsheet = spreadsheet
185
189
  end
186
-
190
+
187
191
  def columns
188
192
  all_cols = []
189
193
  @spreadsheet.columns.each do |column|
190
- all_cols << datum_for(column)
191
- end
194
+ all_cols << datum_for(column)
195
+ end
192
196
  all_cols
193
197
  end
194
-
198
+
195
199
  # This returns a Datum object for a header name. Will return a nil Datum object
196
200
  # for nonexistant column names
197
- #
201
+ #
198
202
  # Parameters:
199
203
  # header name
200
- #
204
+ #
201
205
  # Returns:
202
206
  # Datum object
203
- #
207
+ #
204
208
  def datum_for(col_name)
205
209
  if col_num = @spreadsheet.column_headers.index(col_name)
206
210
  TableFu::Datum.new(self[col_num], col_name, self, @spreadsheet)
@@ -209,7 +213,7 @@ class TableFu
209
213
  end
210
214
  end
211
215
  alias_method :column_for, :datum_for
212
-
216
+
213
217
  # sugar
214
218
  def [](col)
215
219
  if col.is_a?(String)
@@ -218,7 +222,7 @@ class TableFu
218
222
  super(col)
219
223
  end
220
224
  end
221
-
225
+
222
226
  # Comparator for sorting a spreadsheet row.
223
227
  #
224
228
  def <=>(b)
@@ -228,25 +232,25 @@ class TableFu
228
232
  format = @spreadsheet.sorted_by[column]["format"]
229
233
  a = column_for(column).value || ''
230
234
  b = b.column_for(column).value || ''
231
- if format
235
+ if format
232
236
  a = TableFu::Formatting.send(format, a) || ''
233
237
  b = TableFu::Formatting.send(format, b) || ''
234
238
  end
235
239
  result = a <=> b
236
240
  result = -1 if result.nil?
237
- result = result * -1 if order == 'descending'
241
+ result = result * -1 if order == 'descending'
238
242
  result
239
243
  else
240
244
  -1
241
245
  end
242
246
  end
243
-
247
+
244
248
  end
245
249
  # A Datum is an individual cell in the TableFu::Row
246
250
  class Datum
247
251
 
248
252
  attr_reader :options, :column_name
249
-
253
+
250
254
  # Each piece of datum should know where it is by column and row number, along
251
255
  # with the spreadsheet it's apart of. There's probably a better way to go
252
256
  # about doing this. Subclass?
@@ -256,7 +260,7 @@ class TableFu
256
260
  @row = row
257
261
  @spreadsheet = spreadsheet
258
262
  end
259
-
263
+
260
264
  # Our standard formatter for the datum
261
265
  #
262
266
  # Returns:
@@ -264,7 +268,7 @@ class TableFu
264
268
  #
265
269
  # First we test to see if this Datum has a macro attached to it. If so
266
270
  # we let the macro method do it's magic
267
- #
271
+ #
268
272
  # Then we test for a simple formatter method.
269
273
  #
270
274
  # And finally we return a empty string object or the value.
@@ -278,8 +282,8 @@ class TableFu
278
282
  @datum || ''
279
283
  end
280
284
  end
281
-
282
- # Returns the macro'd format if there is one
285
+
286
+ # Returns the macro'd format if there is one
283
287
  #
284
288
  # Returns:
285
289
  # The macro value if it exists, otherwise nil
@@ -288,26 +292,26 @@ class TableFu
288
292
  # Then get a array of the values in the columns listed as arguments
289
293
  # Splat the arguments to the macro method.
290
294
  # Example:
291
- # @spreadsheet.col_opts[:formatting] =
295
+ # @spreadsheet.col_opts[:formatting] =
292
296
  # {'Total Appropriation' => :currency,
293
297
  # 'AppendedColumn' => {'method' => 'append', 'arguments' => ['Projects','State']}}
294
- #
298
+ #
295
299
  # in the above case we handle the AppendedColumn in this method
296
300
 
297
301
  if @spreadsheet.formatting && @spreadsheet.formatting[@column_name].is_a?(Hash)
298
302
  method = @spreadsheet.formatting[@column_name]['method']
299
303
  arguments = @spreadsheet.formatting[@column_name]['arguments'].inject([]) do |arr,arg|
300
- arr << @row.column_for(arg)
304
+ arr << @row.column_for(arg)
301
305
  arr
302
306
  end
303
307
  TableFu::Formatting.send(method, *arguments)
304
308
  end
305
309
  end
306
-
307
- # Returns the raw value of a datum
310
+
311
+ # Returns the raw value of a datum
308
312
  #
309
313
  # Returns:
310
- # raw value of the datum, could be nil
314
+ # raw value of the datum, could be nil
311
315
  def value
312
316
  if @datum =~ /[0-9]+/
313
317
  @datum.to_i
@@ -320,21 +324,21 @@ class TableFu
320
324
  #
321
325
  # First Option
322
326
  # We have a column option by that method name and it applies to this column
323
- # Example -
327
+ # Example -
324
328
  # >> @data.column_name
325
329
  # => 'Total'
326
330
  # >> @datum.style
327
331
  # Finds col_opt[:style] = {'Total' => 'text-align:left;'}
328
332
  # => 'text-align:left;'
329
- #
333
+ #
330
334
  # Second Option
331
335
  # We have a column option by that method name, but no attribute
332
336
  # >> @data.column_name
333
337
  # => 'Total'
334
- # >> @datum.style
338
+ # >> @datum.style
335
339
  # Finds col_opt[:style] = {'State' => 'text-align:left;'}
336
340
  # => ''
337
- #
341
+ #
338
342
  # Third Option
339
343
  # The boolean
340
344
  # >> @data.invisible?
@@ -361,7 +365,7 @@ class TableFu
361
365
  super
362
366
  end
363
367
  end
364
-
368
+
365
369
  private
366
370
 
367
371
  # Enable string or symbol key access to col_opts
@@ -378,7 +382,7 @@ class TableFu
378
382
  Hash.new {|hash,key| hash[key.to_s] if Symbol === key }
379
383
  end
380
384
  end
381
-
385
+
382
386
  # A header object needs to be a special kind of Datum, and
383
387
  # we may want to extend this further, but currently we just
384
388
  # need to ensure that when to_s is called on a @Header@ object
@@ -388,9 +392,9 @@ class TableFu
388
392
  def to_s
389
393
  @datum
390
394
  end
391
-
395
+
392
396
  end
393
-
397
+
394
398
  end
395
399
 
396
400
  $:.unshift(File.dirname(__FILE__)) unless
@@ -1,6 +1,5 @@
1
1
  require 'spec'
2
- require 'spec/spec_helper'
3
- require 'fastercsv'
2
+ require 'spec_helper.rb'
4
3
 
5
4
 
6
5
  describe TableFu do
@@ -17,19 +16,19 @@ CSV
17
16
  s.formatting = {'Cost' => 'currency'}
18
17
  s.sorted_by = {'Project' => {'order' => 'descending'}}
19
18
  s.columns = ['Date', 'Project', 'Cost']
20
- end
21
-
19
+ end
20
+
22
21
  end
23
22
 
24
23
  it "should just work" do
25
24
  @spreadsheet.rows[0].column_for('Cost').to_s.should == '$45.00'
26
25
  @spreadsheet.rows[0].columns[1].to_s.should == 'Motorized Bar Stool'
27
26
  end
28
-
27
+
29
28
  it 'should open a file if passed one' do
30
29
  @spreadsheet = TableFu.new(File.open('spec/assets/test.csv')).rows[0].column_for('State').to_s.should eql "Alabama"
31
30
  end
32
-
31
+
33
32
  it "should populate headers if we don't tell it which headers to use" do
34
33
  TableFu.new(@csv).headers.should_not be_nil
35
34
  end
@@ -1,4 +1,3 @@
1
- $:.unshift File.expand_path(File.join(File.dirname(__FILE__)))
2
-
1
+ $:.unshift File.expand_path(File.join(File.dirname(__FILE__), '..'))
2
+ require 'rubygems'
3
3
  require 'lib/table_fu'
4
- require 'rubygems'
@@ -1,6 +1,5 @@
1
1
  require 'spec'
2
- require 'spec/spec_helper'
3
- require 'fastercsv'
2
+ require 'spec_helper'
4
3
 
5
4
 
6
5
  describe TableFu do
@@ -19,7 +18,7 @@ describe TableFu do
19
18
  it 'should give me back a column by it\'s header name' do
20
19
  @spreadsheet.rows[0].column_for("State").to_s.should == "Alabama"
21
20
  end
22
-
21
+
23
22
  it 'should sort rows' do
24
23
  @spreadsheet.sorted_by = {'State' => {"order" => 'descending'}}
25
24
  @spreadsheet.rows[0].column_for("State").to_s.should eql "Wyoming"
@@ -28,8 +27,8 @@ describe TableFu do
28
27
  @spreadsheet.sorted_by = {'Representative' => {"order" => 'ascending', "format" => 'last_name'}}
29
28
  @spreadsheet.rows[2].column_for("Representative").to_s.should eql "Jo Bonner"
30
29
 
31
-
32
- @spreadsheet.col_opts[:columns] = {'State', 'Party', 'Total Appropriations', 'URL'}
30
+
31
+ @spreadsheet.col_opts[:columns] = ['State', 'Party', 'Total Appropriations', 'URL']
33
32
  @spreadsheet.rows.each do |row|
34
33
  row.columns.each do |column|
35
34
  if column.column_name == 'URL'
@@ -40,9 +39,9 @@ describe TableFu do
40
39
  column.style.should_not be_nil
41
40
  end
42
41
  end
43
-
42
+
44
43
  end
45
-
44
+
46
45
  it 'should sort rows with numerals' do
47
46
  @spreadsheet.sorted_by = {'Projects' => {"order" => 'ascending'}}
48
47
  sorted = @spreadsheet.rows.map do |row|
@@ -50,7 +49,7 @@ describe TableFu do
50
49
  end
51
50
  sorted.should eql [4, 5, 10, 12, 20, 49, nil]
52
51
  end
53
-
52
+
54
53
  it 'should be able to contain only the rows I want, after sorting' do
55
54
  @spreadsheet.sorted_by = {'State' => {"order" => 'ascending'}}
56
55
  @spreadsheet.only!(1..2)
@@ -66,7 +65,7 @@ describe TableFu, 'with a complicated setup' do
66
65
  before :all do
67
66
  csv = FasterCSV.parse(File.open('spec/assets/test.csv'))
68
67
  @spreadsheet = TableFu.new(csv)
69
- @spreadsheet.col_opts[:formatting] = {'Total Appropriation' => :currency,
68
+ @spreadsheet.col_opts[:formatting] = {'Total Appropriation' => :currency,
70
69
  "Representative" => :last_name_first_name}
71
70
  @spreadsheet.col_opts[:style] = {'Leadership' => "text-align: left;", 'URL' => 'text-align: right;'}
72
71
  @spreadsheet.col_opts[:invisible] = ['URL']
@@ -95,12 +94,12 @@ describe TableFu, 'with a complicated setup' do
95
94
  @spreadsheet.rows[2].column_for('State').to_s.should eql "New Jersey"
96
95
  @spreadsheet.rows[0].column_for('State').to_s.should eql "Wyoming"
97
96
  end
98
-
97
+
99
98
  it 'should have some sugar' do
100
99
  @spreadsheet.rows[3]['State'].to_s.should eql "Georgia"
101
-
100
+
102
101
  end
103
-
102
+
104
103
  it 'should total a column' do
105
104
  @spreadsheet.total_for("Total Appropriation").value.should eql 16640189309
106
105
  @spreadsheet.total_for("Total Appropriation").to_s.should eql "$16,640,189,309"
@@ -111,12 +110,12 @@ describe TableFu, 'with a complicated setup' do
111
110
  @spreadsheet.rows[0].column_for("Total Appropriation").value.should eql 138526141
112
111
  @spreadsheet.rows[4].column_for("Representative").to_s.should eql "Nunes, Devin"
113
112
  end
114
-
113
+
115
114
  it 'should format a header' do
116
115
  @spreadsheet.headers[1].style.should eql 'text-align: left;'
117
116
  @spreadsheet.headers[4].style.should eql 'text-align: right;'
118
117
  end
119
-
118
+
120
119
  it 'should not care what kind of keys the hash has' do
121
120
  @spreadsheet = TableFu.new(File.open('spec/assets/test.csv'))
122
121
  @spreadsheet.col_opts["style"] = {'Leadership' => "text-align: left;", 'URL' => 'text-align: right;'}
@@ -124,7 +123,7 @@ describe TableFu, 'with a complicated setup' do
124
123
  @spreadsheet.rows[0].column_for('Leadership').style.should ==
125
124
  @spreadsheet.col_opts["style"]['Leadership']
126
125
  end
127
-
126
+
128
127
 
129
128
 
130
129
  end
@@ -163,7 +162,7 @@ describe TableFu, "with faceting" do
163
162
  end
164
163
 
165
164
  it "should keep the formatting" do
166
-
165
+
167
166
  @faceted_spreadsheets[1].rows[1].column_for('Total Appropriation').to_s.should eql "$25,320,127"
168
167
  @faceted_spreadsheets[1].rows[1].column_for('Projects').style.should eql "text-align:left;"
169
168
  end
@@ -174,15 +173,15 @@ end
174
173
  describe TableFu, 'with macro columns' do
175
174
 
176
175
  class TableFu::Formatting
177
-
176
+
178
177
  class<<self
179
178
 
180
179
  def append(first, second)
181
180
  "#{first}#{second}"
182
181
  end
183
-
182
+
184
183
  end
185
-
184
+
186
185
  end
187
186
 
188
187
 
@@ -195,13 +194,13 @@ describe TableFu, 'with macro columns' do
195
194
  @spreadsheet.sorted_by = {'Projects' => {'order' => 'descending'}}
196
195
  @spreadsheet.col_opts[:columns] = ['State', 'Total Appropriation', 'Projects', 'MacroColumn']
197
196
  end
198
-
197
+
199
198
  it "should let us specify a macro for a column" do
200
199
  @spreadsheet.rows[1].column_for('MacroColumn').to_s.should eql '20Arizona'
201
200
  end
202
-
201
+
203
202
  it "should keep the rows in order" do
204
- @spreadsheet.rows[0].column_for('Projects').value.should eql 49
203
+ @spreadsheet.rows[0].column_for('Projects').value.should eql 49
205
204
  @spreadsheet.rows[1].column_for('Total Appropriation').to_s.should eql '$42,367,198'
206
205
  end
207
206
 
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{table_fu}
8
- s.version = "0.3.0"
8
+ s.version = "0.3.1"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Mark Percival", "Jeff Larson"]
12
- s.date = %q{2010-05-26}
12
+ s.date = %q{2010-11-19}
13
13
  s.description = %q{A library for manipulating tables as arrays}
14
14
  s.email = %q{jeff.larson@gmail.com}
15
15
  s.extra_rdoc_files = [
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: table_fu
3
3
  version: !ruby/object:Gem::Version
4
- hash: 19
4
+ hash: 17
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 3
9
- - 0
10
- version: 0.3.0
9
+ - 1
10
+ version: 0.3.1
11
11
  platform: ruby
12
12
  authors:
13
13
  - Mark Percival
@@ -16,7 +16,7 @@ autorequire:
16
16
  bindir: bin
17
17
  cert_chain: []
18
18
 
19
- date: 2010-05-26 00:00:00 -04:00
19
+ date: 2010-11-19 00:00:00 -05:00
20
20
  default_executable:
21
21
  dependencies:
22
22
  - !ruby/object:Gem::Dependency