| 1 |
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" |
|---|
| 2 |
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> |
|---|
| 3 |
|
|---|
| 4 |
<html xmlns="http://www.w3.org/1999/xhtml"> |
|---|
| 5 |
<head> |
|---|
| 6 |
<meta http-equiv="content-type" content="text/html;charset=UTF-8" /> |
|---|
| 7 |
<title>Ruby Reports</title> |
|---|
| 8 |
<link href="ruport.css" rel="stylesheet" type="text/css" /> |
|---|
| 9 |
<script src="http://www.google-analytics.com/urchin.js" type="text/javascript"> |
|---|
| 10 |
</script> |
|---|
| 11 |
<script type="text/javascript"> |
|---|
| 12 |
_uacct = "UA-2193841-1"; |
|---|
| 13 |
urchinTracker(); |
|---|
| 14 |
</script> |
|---|
| 15 |
</head> |
|---|
| 16 |
|
|---|
| 17 |
<body> |
|---|
| 18 |
<div id="wrapper"> |
|---|
| 19 |
<div id="mainNav"> |
|---|
| 20 |
<ul> |
|---|
| 21 |
<li><a href="ruport.html">Overview</a></li> |
|---|
| 22 |
<li><a href="examples.html">Examples</a></li> |
|---|
| 23 |
<li><a href="http://api.rubyreports.org/">API Docs</a></li> |
|---|
| 24 |
<li><a href="http://ruportbook.com">Book</a></li> |
|---|
| 25 |
<li><a href="http://list.rubyreports.org">Mailing List</a></li> |
|---|
| 26 |
<li><a href="resources.html">Resources</a></li> |
|---|
| 27 |
<li><a href="http://code.rubyreports.org">Development</a></li> |
|---|
| 28 |
</ul> |
|---|
| 29 |
</div> |
|---|
| 30 |
<div id="logo"> |
|---|
| 31 |
<h1><span>Ruport: Ruby Reports</span></h1> |
|---|
| 32 |
<h2>A simple, extensible reporting system built for Rubyists</h2> |
|---|
| 33 |
</div> |
|---|
| 34 |
<div id="mainContent"> |
|---|
| 35 |
<h3>Ruport 1.2 Release Notes, 2007.08.28</h3> |
|---|
| 36 |
<p>This release provides a number of enhancements and updates to the |
|---|
| 37 |
<b>ruport</b> package, the core library for the Ruby Reports project. |
|---|
| 38 |
</p><p> |
|---|
| 39 |
Though we aimed for high backwards compatibility with the 1.0 codebase, |
|---|
| 40 |
there have been some small API changes in places. These notes will |
|---|
| 41 |
show these incompatibilities as well as a sampling of the new features |
|---|
| 42 |
we've added. |
|---|
| 43 |
</p> |
|---|
| 44 |
|
|---|
| 45 |
<h3>Install Instructions: </h3> |
|---|
| 46 |
|
|---|
| 47 |
<p>Ruport and its supporting toolset, ruport-util are packaged as gems, |
|---|
| 48 |
making installation trival: </p> |
|---|
| 49 |
|
|---|
| 50 |
<pre> |
|---|
| 51 |
gem install ruport -y |
|---|
| 52 |
gem install ruport-util -y |
|---|
| 53 |
</pre> |
|---|
| 54 |
|
|---|
| 55 |
<p>If you're not already familiar with Ruport, you might want to have |
|---|
| 56 |
a look at some <a href="examples.html">simple examples</a></p> |
|---|
| 57 |
|
|---|
| 58 |
<h3>List of changes: </h3> |
|---|
| 59 |
|
|---|
| 60 |
<p><b>API Breakage</b></p> |
|---|
| 61 |
<ul> |
|---|
| 62 |
<li>acts_as_reportable now uses real association names</li> |
|---|
| 63 |
<li>Data::Table constructors now yield Data::Feeder objects instead of Data::Table</li> |
|---|
| 64 |
<li>append_hash, append_array, and append_record removed from Data::Table</li> |
|---|
| 65 |
<li>Renderer::Hooks changed signature for renderable_data() to renderable_data(format)</li> |
|---|
| 66 |
<li>Formatter::PDF#draw_text no longer changes the position of the drawing cursor</li> |
|---|
| 67 |
<li>Ruport 0.7 style stage building syntax removed</li> |
|---|
| 68 |
</ul> |
|---|
| 69 |
|
|---|
| 70 |
<p><b>Enhancements to existing features</b></p> |
|---|
| 71 |
<ul> |
|---|
| 72 |
<li>Table#sort_rows_by now allows nil objects and allows |
|---|
| 73 |
ascending / descending order</li> |
|---|
| 74 |
<li>Table constructors now accept :filters and :transforms Procs</li> |
|---|
| 75 |
<li>AAR's report_table() now accept :filters and :transforms Procs</li> |
|---|
| 76 |
<li>Grouping constructor accepts :order, and new Grouping#sort_grouping_by method</li> |
|---|
| 77 |
<li>Formatter::PDF#draw_table now exposes column heading formatting options</li> |
|---|
| 78 |
<li>Renderers can now be passed :file => "somefile.txt" to save output</li> |
|---|
| 79 |
</ul> |
|---|
| 80 |
|
|---|
| 81 |
<p><b>Brand New Features</b></p> |
|---|
| 82 |
|
|---|
| 83 |
<ul> |
|---|
| 84 |
<li>Data::Feeder allows for custom transformations and filters on data</li> |
|---|
| 85 |
<li>Grouping#sigma added (Thanks Dave Nelson)</li> |
|---|
| 86 |
<li>Formatter::PDF#draw_text! will draw text at an absolute position, |
|---|
| 87 |
ignoring margins</li> |
|---|
| 88 |
<li>Formatter::Template provides a simple templating system for |
|---|
| 89 |
renderer options</li> |
|---|
| 90 |
</ul> |
|---|
| 91 |
|
|---|
| 92 |
<h3>Notes on backwards incompatible changes: </h3> |
|---|
| 93 |
|
|---|
| 94 |
<p>The following features are not compatible with 1.0, and may cause |
|---|
| 95 |
problems when running older Ruport code on 1.2 |
|---|
| 96 |
</p> |
|---|
| 97 |
|
|---|
| 98 |
<p><b>- acts_as_reportable now uses real association names</b></p> |
|---|
| 99 |
|
|---|
| 100 |
<p> |
|---|
| 101 |
In Ruport 1.0, acts_as_reportable used to do a transformation of model |
|---|
| 102 |
class names to name its associations in report tables. It now uses |
|---|
| 103 |
the proper association name to qualify attribute names. This will |
|---|
| 104 |
break any old code that relied on the earlier convention, but eliminates |
|---|
| 105 |
ambiguity issues. |
|---|
| 106 |
</p> |
|---|
| 107 |
|
|---|
| 108 |
<p> |
|---|
| 109 |
For example, say you had two models, Customer and CustomerAddress |
|---|
| 110 |
defined as follows: |
|---|
| 111 |
</p> |
|---|
| 112 |
|
|---|
| 113 |
<pre> |
|---|
| 114 |
class Customer |
|---|
| 115 |
has_many :addresses, :class_name => "CustomerAddress" |
|---|
| 116 |
end |
|---|
| 117 |
|
|---|
| 118 |
class CustomerAddress |
|---|
| 119 |
belongs_to :customer |
|---|
| 120 |
end |
|---|
| 121 |
</pre> |
|---|
| 122 |
|
|---|
| 123 |
<p>If you do:</p> |
|---|
| 124 |
|
|---|
| 125 |
<pre> |
|---|
| 126 |
Customer.report_table(:all, :include => :addresses) |
|---|
| 127 |
</pre> |
|---|
| 128 |
|
|---|
| 129 |
<p> |
|---|
| 130 |
Previously, the columns from CustomerAddress would have been qualified as |
|---|
| 131 |
customer_address.attribute_name whereas now, they would be |
|---|
| 132 |
addresses.attribute_name |
|---|
| 133 |
</p> |
|---|
| 134 |
|
|---|
| 135 |
<p><b>- Data::Table constructors now yield Data::Feeder objects</b></p> |
|---|
| 136 |
|
|---|
| 137 |
<p>You may have been using block form table constructors in your report, |
|---|
| 138 |
which look like this:</p> |
|---|
| 139 |
|
|---|
| 140 |
<pre> |
|---|
| 141 |
table = Table(%w[month year]) { |t| t << ["June",2006], ["July", 2007] } |
|---|
| 142 |
|
|---|
| 143 |
table = Table("foo.csv") { |table,row| table << row } |
|---|
| 144 |
</pre> |
|---|
| 145 |
|
|---|
| 146 |
<p> |
|---|
| 147 |
These were mostly used to build up a table structure from some source data |
|---|
| 148 |
via <tt>Table#<<</tt> within a block local context. |
|---|
| 149 |
</p><p> |
|---|
| 150 |
We have now created a <tt>Data::Feeder</tt> object which allows you to |
|---|
| 151 |
do customized data transformations and filtering. These new features |
|---|
| 152 |
are described later in these notes, but what is important here is that all |
|---|
| 153 |
Table constructors now yield a Data::Feeder object instead of a Table. |
|---|
| 154 |
</p><p> |
|---|
| 155 |
A default Data::Feeder#<< will behave like Data::Table#<<. so you may not |
|---|
| 156 |
need to modify your code if this is all it needs. |
|---|
| 157 |
</p><p> |
|---|
| 158 |
However, if you need other table methods, you will need to call |
|---|
| 159 |
Data::Feeder#data to access the table object. For example: |
|---|
| 160 |
<p> |
|---|
| 161 |
|
|---|
| 162 |
<pre> |
|---|
| 163 |
table = Table(%w[month year]) { |feeder| |
|---|
| 164 |
feeder << ["June",2007] |
|---|
| 165 |
feeder << ["July",2007] |
|---|
| 166 |
@months = feeder.data.column("month") |
|---|
| 167 |
} |
|---|
| 168 |
</pre> |
|---|
| 169 |
|
|---|
| 170 |
<p>Of course, this change was meant to make life easier, not harder. See |
|---|
| 171 |
the Data::Feeder examples later in these notes for details.</p> |
|---|
| 172 |
|
|---|
| 173 |
<p><b>- Renderer::Hooks has changed</b></p> |
|---|
| 174 |
|
|---|
| 175 |
<p>In Ruport 1.0, you could do something like this to tie the formatting |
|---|
| 176 |
system to an arbitrary class.</p> |
|---|
| 177 |
|
|---|
| 178 |
<pre> |
|---|
| 179 |
class MyClass |
|---|
| 180 |
include Ruport::Renderer::Hooks |
|---|
| 181 |
renders_as_table |
|---|
| 182 |
|
|---|
| 183 |
def renderable_data |
|---|
| 184 |
# return some table |
|---|
| 185 |
end |
|---|
| 186 |
end |
|---|
| 187 |
</pre> |
|---|
| 188 |
|
|---|
| 189 |
<p>We've found that sometimes you'll want to do specialized processing |
|---|
| 190 |
of your data based on the intended rendering format without creating |
|---|
| 191 |
a custom formatter object. To accommodate for this, we've changed the |
|---|
| 192 |
signature of the renderable_data hook to accept a format parameter:</p> |
|---|
| 193 |
|
|---|
| 194 |
<pre> |
|---|
| 195 |
class MyClass |
|---|
| 196 |
include Ruport::Renderer::Hooks |
|---|
| 197 |
renders_as_table |
|---|
| 198 |
|
|---|
| 199 |
def renderable_data(format) |
|---|
| 200 |
# return some table |
|---|
| 201 |
end |
|---|
| 202 |
end |
|---|
| 203 |
</pre> |
|---|
| 204 |
|
|---|
| 205 |
<p>The <tt>format</tt> parameter is simply the format you pass to |
|---|
| 206 |
<tt>as()</tt> when you are rendering your data. There is of course, |
|---|
| 207 |
no requirement to make use of the format, it is just there for your |
|---|
| 208 |
convenience.</p> |
|---|
| 209 |
|
|---|
| 210 |
<p>Because rope previously generated the 1.0 compatible code, this is |
|---|
| 211 |
the source of incompatibility between Ruport 1.2 and any ruport-util |
|---|
| 212 |
release previous to 0.8.0. If you are not using rope to generate your |
|---|
| 213 |
report classes, you may not need to upgrade ruport-util, though it |
|---|
| 214 |
is probably a good idea to do so anyway</p> |
|---|
| 215 |
|
|---|
| 216 |
|
|---|
| 217 |
<h3>Examples and discussion of other changes: </h3> |
|---|
| 218 |
|
|---|
| 219 |
<p>Nearly all of the enhancements to Ruport since 1.0 have been driven |
|---|
| 220 |
by real needs in our work. Below are some examples which use |
|---|
| 221 |
the new features in 1.2, in no particular order</p> |
|---|
| 222 |
|
|---|
| 223 |
<p><b>- Simplifying transformations and filters with Data::Feeder</b></p> |
|---|
| 224 |
|
|---|
| 225 |
<p>We often want to constrain our data as it is being aggregated rather |
|---|
| 226 |
than after we've collected it all. Data::Feeder provides a simple |
|---|
| 227 |
proxy object that allows us to do exactly that.</p> |
|---|
| 228 |
|
|---|
| 229 |
<pre> |
|---|
| 230 |
t = Table(%w[a b c]) |
|---|
| 231 |
feeder = Ruport::Data::Feeder.new(t) |
|---|
| 232 |
feeder.filter { |r| r.a < 10 } |
|---|
| 233 |
feeder << [1,2,3] << [9,6,1] << [11,3,2] << [2,1,7] |
|---|
| 234 |
t.column("a") #=> [1, 9, 2] |
|---|
| 235 |
</pre> |
|---|
| 236 |
|
|---|
| 237 |
<p>You can create multiple feeders over the same source data, and define |
|---|
| 238 |
multiple filters on a single filter. You can also set up filters to |
|---|
| 239 |
work on an initial data set via the Table constructor: </p> |
|---|
| 240 |
|
|---|
| 241 |
<pre> |
|---|
| 242 |
t = Table(%w[a b c], :data => [[1,2,3],[9,6,1],[11,3,2],[2,1,7]], |
|---|
| 243 |
:filters => lambda { |r| r.a < 10 }) |
|---|
| 244 |
t.column("a") #=> [1,9,2] |
|---|
| 245 |
</pre> |
|---|
| 246 |
|
|---|
| 247 |
<p>You can also get back a feeder object from the Table constructor and |
|---|
| 248 |
build up your result set iteratively.</p> |
|---|
| 249 |
|
|---|
| 250 |
<pre> |
|---|
| 251 |
t = Table(%w[a b c]) do |feeder| |
|---|
| 252 |
feeder.filter { |r| r.a < 10 } |
|---|
| 253 |
feeder << [1,2,3] << [9,6,1] << [11,3,2] << [2,1,7] |
|---|
| 254 |
end |
|---|
| 255 |
t.column("a") #=> [1,9,2] |
|---|
| 256 |
</pre> |
|---|
| 257 |
|
|---|
| 258 |
<p>Feeders also provide a way to do transformations on your data:</p> |
|---|
| 259 |
|
|---|
| 260 |
<pre> |
|---|
| 261 |
t = Table(%w[a b c]) |
|---|
| 262 |
feeder = Ruport::Data::Feeder.new(t) |
|---|
| 263 |
feeder.transform { |r| r.a = "a: #{r.a}" } |
|---|
| 264 |
feeder << [1,2,3] << [9,6,1] << [11,3,2] << [2,1,7] |
|---|
| 265 |
>> t.column("a") |
|---|
| 266 |
=> ["a: 1", "a: 9", "a: 11", "a: 2"] |
|---|
| 267 |
</pre> |
|---|
| 268 |
|
|---|
| 269 |
<p>Like filters, these can be specified via a :transforms option to the |
|---|
| 270 |
table constructor to tranform an initial data set, or used within the |
|---|
| 271 |
block context to alter the data iteratively.</p> |
|---|
| 272 |
|
|---|
| 273 |
<p><b>- Abstract formatting options via Formatter::Template</b></p> |
|---|
| 274 |
|
|---|
| 275 |
<p>Templates are an entirely new feature to Ruport 1.2. They allow you |
|---|
| 276 |
to define a reusable set of formatting options. You can create |
|---|
| 277 |
multiple templates with different options and specify which one should |
|---|
| 278 |
be used when output is rendered.</p> |
|---|
| 279 |
|
|---|
| 280 |
<p>You define a template by using the <code>create</code> method of |
|---|
| 281 |
Ruport::Formatter::Template.</p> |
|---|
| 282 |
|
|---|
| 283 |
<pre> |
|---|
| 284 |
Ruport::Formatter::Template.create(:simple) do |t| |
|---|
| 285 |
t.page_format = { |
|---|
| 286 |
:size => "LETTER", |
|---|
| 287 |
:layout => :landscape |
|---|
| 288 |
} |
|---|
| 289 |
end |
|---|
| 290 |
</pre> |
|---|
| 291 |
|
|---|
| 292 |
<p>When creating the template, you can specify whatever options you |
|---|
| 293 |
want, but your formatter needs to know what to do with them or else |
|---|
| 294 |
they will just be ignored. You define an <code>apply_template</code> |
|---|
| 295 |
method in your formatter to tell it how to process the template.</p> |
|---|
| 296 |
|
|---|
| 297 |
<pre> |
|---|
| 298 |
class Ruport::Formatter::PDF |
|---|
| 299 |
|
|---|
| 300 |
def apply_template |
|---|
| 301 |
options.paper_size = template.page_format[:size] |
|---|
| 302 |
options.paper_orientation = template.page_format[:layout] |
|---|
| 303 |
end |
|---|
| 304 |
|
|---|
| 305 |
end |
|---|
| 306 |
</pre> |
|---|
| 307 |
|
|---|
| 308 |
<p>To use a template, just specify it using the :template option when |
|---|
| 309 |
you render your output. Note that even if you've defined templates |
|---|
| 310 |
and set up the formatter to use them, they are still optional. If you |
|---|
| 311 |
don't specify a :template option, <code>apply_template</code> simply |
|---|
| 312 |
won't be called.</p> |
|---|
| 313 |
|
|---|
| 314 |
<pre> |
|---|
| 315 |
t = Table(%w[a b c]) << [1,2,3] << [1,4,6] << [2,3,4] |
|---|
| 316 |
puts t.to_pdf(:template => :simple) #=> uses the :simple template |
|---|
| 317 |
puts t.to_pdf #=> doesn't use a template |
|---|
| 318 |
</pre> |
|---|
| 319 |
|
|---|
| 320 |
<p>You can also derive a template from another, pre-existing |
|---|
| 321 |
template, using the :base option to |
|---|
| 322 |
Ruport::Formatter::Template.create.</p> |
|---|
| 323 |
|
|---|
| 324 |
<pre> |
|---|
| 325 |
Ruport::Formatter::Template.create(:derived, :base => :simple) |
|---|
| 326 |
</pre> |
|---|
| 327 |
|
|---|
| 328 |
<p>One thing that the templates have allowed us to do is to |
|---|
| 329 |
standardize the interface to the different built-in formatters. Each |
|---|
| 330 |
formatter has an <code>apply_template</code> method predefined that |
|---|
| 331 |
will accept a standard set of options. If, however, you don't like |
|---|
| 332 |
the predefined set up, it's easy to specify your own interface by |
|---|
| 333 |
overriding the existing <code>apply_template</code> methods. This |
|---|
| 334 |
example shows a number of the options being used.</p> |
|---|
| 335 |
|
|---|
| 336 |
<pre> |
|---|
| 337 |
Ruport::Formatter::Template.create(:simple) do |t| |
|---|
| 338 |
t.page_format = { |
|---|
| 339 |
:size => "LETTER", |
|---|
| 340 |
:layout => :landscape |
|---|
| 341 |
} |
|---|
| 342 |
t.text_format = { |
|---|
| 343 |
:font_size => 16 |
|---|
| 344 |
} |
|---|
| 345 |
t.table_format = { |
|---|
| 346 |
:font_size => 16, |
|---|
| 347 |
:show_headings => false |
|---|
| 348 |
} |
|---|
| 349 |
t.column_format = { |
|---|
| 350 |
:alignment => :center, |
|---|
| 351 |
:heading => { :justification => :right } |
|---|
| 352 |
} |
|---|
| 353 |
t.grouping_format = { |
|---|
| 354 |
:style => :separated |
|---|
| 355 |
} |
|---|
| 356 |
end |
|---|
| 357 |
</pre> |
|---|
| 358 |
|
|---|
| 359 |
<p><b>- Sorting your Grouping objects</b></p> |
|---|
| 360 |
|
|---|
| 361 |
<p>In Ruport 1.0, Grouping objects were unordered. Though isn't so much |
|---|
| 362 |
an issue for working with the objects, order often becomes significant |
|---|
| 363 |
in output. We've provided a few feature enhancements to make this |
|---|
| 364 |
easier.</p> |
|---|
| 365 |
|
|---|
| 366 |
<p>The most simple case is when you simply want to order your Grouping |
|---|
| 367 |
by the group names.</p> |
|---|
| 368 |
|
|---|
| 369 |
<pre> |
|---|
| 370 |
t = Table(%w[email id]) |
|---|
| 371 |
t << ["aaa@aaa.com",1] << ["bbb@bbb.com",4] << ["aaa@aaa.com",3] |
|---|
| 372 |
g = Grouping(t, :by => "email", :order => :name) |
|---|
| 373 |
g.to_a.map { |name,group| name } #=> ["aaa.aaa.com", "bbb@bbb.com"] |
|---|
| 374 |
</pre> |
|---|
| 375 |
|
|---|
| 376 |
<p>For more complex situations, you can order by an arbitrary block. In |
|---|
| 377 |
this example, we sort the groups by their size:</p> |
|---|
| 378 |
|
|---|
| 379 |
<pre> |
|---|
| 380 |
group_size = lambda { |g| g.size } |
|---|
| 381 |
g = Grouping(t, :by => "email", :order => group_size) |
|---|
| 382 |
g.to_a.map { |name,group| name } #=> ["bbb@bbb.com", "aaa@aaa.com"] |
|---|
| 383 |
</pre> |
|---|
| 384 |
|
|---|
| 385 |
<p>You can also sort the groupings after they have been created, see the |
|---|
| 386 |
sort_grouping_by and sort_grouping_by! methods for details.</p> |
|---|
| 387 |
|
|---|
| 388 |
<p><b>- Table sorting gets smarter</b></p> |
|---|
| 389 |
|
|---|
| 390 |
<p>In Ruport 1.0, Table#sort_rows_by(col) would throw an error if any nils were |
|---|
| 391 |
encountered. They are now simply tacked on to the end of a result set by |
|---|
| 392 |
default.</p> |
|---|
| 393 |
|
|---|
| 394 |
<p>You can also now easily specify the sort order. If you want to reverse |
|---|
| 395 |
the order of sorting, just use something like this:</p> |
|---|
| 396 |
|
|---|
| 397 |
<pre> |
|---|
| 398 |
table.sort_rows_by(:some_column, :order => :descending) |
|---|
| 399 |
</pre> |
|---|
| 400 |
|
|---|
| 401 |
<p><b>- Grouping#sigma </b><p> |
|---|
| 402 |
|
|---|
| 403 |
<p>You can now do sums across Grouping objects in the same manner as you |
|---|
| 404 |
do with Tables:</p> |
|---|
| 405 |
|
|---|
| 406 |
<pre> |
|---|
| 407 |
table = [[1,2,3],[3,4,5],[5,6,7]].to_table(%w[col1 col2 col3]) |
|---|
| 408 |
grouping = Grouping(table, :by => "col1") |
|---|
| 409 |
grouping.sigma("col2") #=> 12 |
|---|
| 410 |
grouping.sigma(0) #=> 12 |
|---|
| 411 |
grouping.sigma { |r| r.col2 + r.col3 } #=> 27 |
|---|
| 412 |
grouping.sigma { |r| r.col2 + 1 } #=> 15 |
|---|
| 413 |
</pre> |
|---|
| 414 |
|
|---|
| 415 |
<p>You can also use sum() instead of sigma(), they do the same thing.</p> |
|---|
| 416 |
|
|---|
| 417 |
<p><b>Easy File Output for Renderers</b></p> |
|---|
| 418 |
|
|---|
| 419 |
<p>We got tired of writing File.open(...), so our formatters now know how |
|---|
| 420 |
to write to file:</p> |
|---|
| 421 |
|
|---|
| 422 |
<pre> |
|---|
| 423 |
Table(%w[a b c], :data => [[1,2,3],[4,5,6]]).to_pdf(:file => "foo.pdf") |
|---|
| 424 |
</pre> |
|---|
| 425 |
|
|---|
| 426 |
<p>This doesn't cover absolutely all the changes made in Ruport 1.2, but |
|---|
| 427 |
hits most of the interesting ones.</p> |
|---|
| 428 |
|
|---|
| 429 |
<h3>Project news: </h3> |
|---|
| 430 |
|
|---|
| 431 |
<p>Over the last few months, we've been busy working on a number of |
|---|
| 432 |
community resources.</p> |
|---|
| 433 |
|
|---|
| 434 |
<p>We've set up <a href="http://code.rubyreports.org">code.rubyreports.org</a> as a portal for Ruport development, |
|---|
| 435 |
linking all of our projects and related projects from our contributors. |
|---|
| 436 |
If you're working on a project that is complimentary to Ruport and want |
|---|
| 437 |
to share some of our resources, let us know.</p> |
|---|
| 438 |
|
|---|
| 439 |
<p>The real core of development over the last few months however has been |
|---|
| 440 |
The Ruport Book. With a large chunk of its content already available |
|---|
| 441 |
online at ruportbook.com, this work represents the most comprehensive |
|---|
| 442 |
and up to date documentation for Ruport, and will give new users a much |
|---|
| 443 |
easier entry point than those who were with us before 1.0</p> |
|---|
| 444 |
|
|---|
| 445 |
<p>We are self-publishing this work under a Creative Commons license, |
|---|
| 446 |
with printed copies available some time before the end of the year. |
|---|
| 447 |
If you like Ruport and find it helpful, the best way to thank us is |
|---|
| 448 |
to pick up a copy when it is available for sale, or |
|---|
| 449 |
<a href="http://www.pledgie.com/campaigns/190">donate to the documentation effort</a>. |
|---|
| 450 |
We will have more details soon, but expect |
|---|
| 451 |
to be taking pre-orders for the printed book in the near future. |
|---|
| 452 |
</p> |
|---|
| 453 |
|
|---|
| 454 |
</div> |
|---|
| 455 |
<div id="secondaryContent"> |
|---|
| 456 |
<h4>Acknowledgements</h4> |
|---|
| 457 |
<p>In addition to Ruport's core team of contributors, the following folks |
|---|
| 458 |
helped make this release possible:</p> |
|---|
| 459 |
|
|---|
| 460 |
<em> |
|---|
| 461 |
Brad Ediger,Gregory Gibson, Imobach González Sosa, Stefan Mahlitz, |
|---|
| 462 |
Jeremy McAnally, Dave Nelson, Emmanuel Oga, Jason Roelofs, Greg Weber |
|---|
| 463 |
</em> |
|---|
| 464 |
|
|---|
| 465 |
<h4>Want To Get Involved?</h4> |
|---|
| 466 |
<p>Our community is friendly and helpful, and happy to walk through whatever |
|---|
| 467 |
problems you might be having on <a href="http://list.rubyreports.org">our mailing list</a>. |
|---|
| 468 |
|
|---|
| 469 |
</p><p>If you're more interested |
|---|
| 470 |
in getting involved in development, the resources at <a href="http://code.rubyreports.org">code.rubyreports.org</a> |
|---|
| 471 |
will get you started</p> |
|---|
| 472 |
|
|---|
| 473 |
<h4>Looking For Professional Support?</h4> |
|---|
| 474 |
<p>Gregory Brown and Michael Milner are currently able to help with a small |
|---|
| 475 |
amount of Ruport related consulting and development. If you're interested |
|---|
| 476 |
in getting some commercial support from the project's lead developers, email |
|---|
| 477 |
<em>jobs at rubyreports dot org</em></p> |
|---|
| 478 |
<h4>Donations Welcome!</h4> |
|---|
| 479 |
<p>If Ruport is helpful to you, please consider making a donation to the Ruby |
|---|
| 480 |
Reports Documentation Effort, which is responsible for <a href="http://ruportbook.com">Ruport Book</p> |
|---|
| 481 |
<a href='http://www.pledgie.com/campaigns/190'><img alt='Click here to lend your support to: Ruby Reports Documentation Effort and make a donation at www.pledgie.com !' src='http://www.pledgie.com/campaigns/190.png?skin_name=chrome' border='0' /></a> |
|---|
| 482 |
</div> |
|---|
| 483 |
<div id="footer"> |
|---|
| 484 |
<p> |
|---|
| 485 |
Ruby Reports is free software under the |
|---|
| 486 |
<a href="http://www.ruby-lang.org/en/LICENSE.txt">License of Ruby</a>. |
|---|
| 487 |
</p> |
|---|
| 488 |
<p> |
|---|
| 489 |
Site designed by Michael Milner. |
|---|
| 490 |
Ruport logo designed by |
|---|
| 491 |
<a href="http://fashionpirat.de/">Daniel Dormann</a>. |
|---|
| 492 |
</p> |
|---|
| 493 |
<p> |
|---|
| 494 |
All web content is licensed under the |
|---|
| 495 |
<a href="http://creativecommons.org/licenses/by-sa/3.0/">Creative |
|---|
| 496 |
Commons Attribution-Sharealike License</a>. |
|---|
| 497 |
</p> |
|---|
| 498 |
</div> |
|---|
| 499 |
</div> |
|---|
| 500 |
</body> |
|---|
| 501 |
</html> |
|---|