root/ruport-www/release_notes-1.2.html

Revision 1177, 19.7 kB (checked in by sandal, 1 year ago)

Release notes

Line 
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>
Note: See TracBrowser for help on using the browser.