C0 code coverage information

Generated on Tue Oct 16 11:40:50 -0400 2007 with rcov 0.8.0


Code reported as executed by Ruby looks like this...
and this: this line is also marked as covered.
Lines considered as run by rcov, but not reported by Ruby, look like this,
and this: these lines were inferred by rcov (using simple heuristics).
Finally, here's a line marked as not executed.
Name Total lines Lines of code Total coverage Code coverage
lib/alexandria/ui/dialogs/new_book_dialog.rb 462 366
15.2% 
7.9% 
  1 # Copyright (C) 2004-2006 Laurent Sansonetti
  2 #
  3 # Alexandria is free software; you can redistribute it and/or
  4 # modify it under the terms of the GNU General Public License as
  5 # published by the Free Software Foundation; either version 2 of the
  6 # License, or (at your option) any later version.
  7 #
  8 # Alexandria is distributed in the hope that it will be useful,
  9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 11 # General Public License for more details.
 12 #
 13 # You should have received a copy of the GNU General Public
 14 # License along with Alexandria; see the file COPYING.  If not,
 15 # write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 16 # Boston, MA 02111-1307, USA.
 17 
 18 require 'gdk_pixbuf2'
 19 
 20 module Alexandria
 21 
 22 class DuplicateBookException < NameError
 23 
 24 end
 25 
 26 module UI
 27     class KeepBadISBNDialog < AlertDialog
 28         include GetText
 29         GetText.bindtextdomain(Alexandria::TEXTDOMAIN, nil, nil, "UTF-8")
 30 
 31         def initialize(parent, book)
 32             super(parent, _("Invalid ISBN '%s'") % book.isbn,
 33                   Gtk::Stock::DIALOG_QUESTION,
 34                   [[Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL],
 35                    [_("_Keep"), Gtk::Dialog::RESPONSE_OK]],
 36                   _("The book titled '%s' has an invalid ISBN, but still " +
 37                     "exists in the providers libraries.  Do you want to " +
 38                     "keep the book but change the ISBN or cancel the add?") \
 39                     % book.title)
 40             self.default_response = Gtk::Dialog::RESPONSE_OK
 41             show_all and @response = run
 42             destroy
 43         end
 44 
 45         def keep?
 46             @response == Gtk::Dialog::RESPONSE_OK
 47         end
 48     end
 49 
 50     class NewBookDialog < GladeBase
 51         include GetText
 52         extend GetText
 53         GetText.bindtextdomain(Alexandria::TEXTDOMAIN, nil, nil, "UTF-8")
 54 
 55         def initialize(parent, selected_library=nil, &block)
 56             super('new_book_dialog.glade')
 57             puts "New Book Dialog" if $DEBUG
 58             @new_book_dialog.transient_for = @parent = parent
 59             @block = block
 60                         @destroyed = false
 61             libraries = Libraries.instance.all_regular_libraries
 62             if selected_library.is_a?(SmartLibrary)
 63                 selected_library = libraries.first
 64             end
 65             @combo_libraries.populate_with_libraries(libraries,
 66                                                      selected_library)
 67 
 68             @treeview_results.model = Gtk::ListStore.new(String, String,
 69                 Gdk::Pixbuf)
 70             @treeview_results.selection.mode = Gtk::SELECTION_MULTIPLE
 71             @treeview_results.selection.signal_connect('changed') do
 72                 @button_add.sensitive = true
 73             end
 74 
 75             renderer = Gtk::CellRendererPixbuf.new
 76             col = Gtk::TreeViewColumn.new("", renderer)
 77             col.set_cell_data_func(renderer) do |column, cell, model, iter|
 78                 pixbuf = iter[2]
 79                 max_height = 25
 80 
 81                 if pixbuf.height > max_height
 82                     new_width = pixbuf.width * (max_height.to_f / pixbuf.height)
 83                     pixbuf = pixbuf.scale(new_width, max_height)
 84                 end
 85 
 86                 cell.pixbuf = pixbuf
 87             end
 88             @treeview_results.append_column(col)
 89 
 90             col = Gtk::TreeViewColumn.new("", Gtk::CellRendererText.new,
 91                                           :text => 0)
 92             @treeview_results.append_column(col)
 93 
 94             @combo_search.active = 0
 95 
 96             # Re-select the last selected criterion.
 97             begin
 98                 @title_radiobutton.active = @@last_criterion_was_not_isbn
 99             rescue NameError
100                 @@last_criterion_was_not_isbn = false
101             end
102 
103             if @@last_criterion_was_not_isbn
104                 @entry_search.grab_focus
105             else
106                 @entry_isbn.grab_focus
107             end
108 
109             @find_thread = nil
110             @image_thread = nil
111 
112             @new_book_dialog.signal_connect("destroy") {
113                 @new_book_dialog.destroy
114                 @destroyed = true
115             }
116         end
117 
118         def on_criterion_toggled(item)
119             return unless item.active?
120             if is_isbn = item == @isbn_radiobutton
121                 @latest_size = @new_book_dialog.size
122                 @new_book_dialog.resizable = false
123             else
124                 @new_book_dialog.resizable = true
125                 @new_book_dialog.resize(*@latest_size) unless @latest_size.nil?
126             end
127             @entry_isbn.sensitive = is_isbn
128             @combo_search.sensitive = !is_isbn
129             @entry_search.sensitive = !is_isbn
130             @button_find.sensitive = !is_isbn
131             @scrolledwindow.visible = !is_isbn
132             on_changed(is_isbn ? @entry_isbn : @entry_search)
133             unless is_isbn
134                 @button_add.sensitive =
135                     @treeview_results.selection.count_selected_rows > 0
136             end
137 
138             # Remember the last criterion selected (so that we can re-select
139             # it when the dialog opens again).
140             @@last_criterion_was_not_isbn = !is_isbn
141         end
142 
143         def on_changed(entry)
144             ok = !entry.text.strip.empty?
145             decode_cuecat?(@entry_isbn) if entry == @entry_isbn
146             (entry == @entry_isbn ? @button_add : @button_find).sensitive = ok
147         end
148 
149         def image_error_dialog(error)
150             ErrorDialog.new(
151                 @parent,
152                 _("A problem occurred while downloading images"),
153                 error)
154         end
155 
156         def get_images_async
157                 puts "get_images_async" if $DEBUG
158             @images = {}
159             @image_error = nil
160             @image_thread = Thread.new do
161                 puts "New @image_thread #{Thread.current}" if $DEBUG
162                 begin
163                         @results.each_with_index do |result, i|
164                         uri = result[1]
165                         if uri
166                                 if URI.parse(uri).scheme.nil?
167                                 File.open(uri, "r") do |io|
168                                     @images[i] = io.read
169                                 end
170                             else
171                                 @images[i] = URI.parse(uri).read
172                             end
173                         end
174                     end
175                 rescue => e
176                     @image_error = e.message
177                 end
178             end
179 
180             Gtk.timeout_add(100) do
181                 if @image_error
182                     image_error_dialog(@image_error)
183                 else
184                     @images.each_pair do |key, value|
185                         begin
186                             loader = Gdk::PixbufLoader.new
187                             loader.last_write(value)
188                             pixbuf = loader.pixbuf
189 
190                             if pixbuf.width > 1
191                                 iter = @treeview_results.model.get_iter(key.to_s)
192                                 unless @treeview_results.model.iter_is_valid?(iter)
193                                         raise "Iter is invalid! %s" % iter
194                                 end
195                                 iter[2] = pixbuf #I bet you this is it!
196                             end
197 
198                             @images.delete(key)
199                         rescue => e
200                             image_error_dialog(e.message)
201                         end
202                     end
203                 end
204 
205                 # Stop if the image download thread has stopped.
206                 if @image_thread.alive?
207                         puts "@image_thread (#{@image_thread}) still alive." if $DEBUG
208                         true
209                 else
210                         puts "@image_thread (#{@image_thread}) asleep now." if $DEBUG
211                         false
212                 end
213             end
214         end
215 
216         def on_find
217                 puts "on_find" if $DEBUG
218             mode = case @combo_search.active
219                 when 0
220                     BookProviders::SEARCH_BY_TITLE
221                 when 1
222                     BookProviders::SEARCH_BY_AUTHORS
223                 when 2
224                     BookProviders::SEARCH_BY_KEYWORD
225             end
226 
227             criterion = @entry_search.text.strip
228             @treeview_results.model.clear
229             puts "TreeStore Model: %s columns; ref_counts: %s" %
230             [@treeview_results.model.n_columns, @treeview_results.model.ref_count] if $DEBUG
231             @new_book_dialog.sensitive = false
232             @find_error = nil
233             @results = nil
234 
235             @find_thread.kill if @find_thread
236             @image_thread.kill if @image_thread
237 
238             @find_thread = Thread.new do
239                 puts "New @find_thread #{Thread.current}" if $DEBUG
240                 begin
241                     @results = Alexandria::BookProviders.search(criterion, mode)
242                     puts "got #{@results.length} results" if $DEBUG
243                 rescue => e
244                     @find_error = e.message
245                 end
246             end
247 
248             Gtk.timeout_add(100) do
249                 # This block copies results into the tree view, or shows an
250                 # error if the search failed.
251 
252                 # Err... continue == false if @find_error
253                 continue = if @find_error
254                     ErrorDialog.new(@parent,
255                                     _("Unable to find matches for your search"),
256                                     @find_error)
257                     false
258                 elsif @results
259                         puts "Got results: #{@results[0]}..." if $DEBUG
260                     @results.each do |book, cover|
261                         s = _("%s, by %s") % [ book.title,
262                                                book.authors.join(', ') ]
263                                                 if @results.find { |book2, cover2|
264                                             book.title == book2.title and
265                                             book.authors == book2.authors
266                                          }.length > 1
267                             s += " (#{book.edition}, #{book.publisher})"
268                         end
269                         puts "Copying %s into tree view." % book.title if $DEBUG
270                                                 iter = @treeview_results.model.append
271                         iter[0] = s
272                         iter[1] = book.ident
273                         iter[2] = Icons::BOOK
274                     end
275 
276                     # Kick off the image download thread.
277                                         if @find_thread.alive?
278                                                 puts "@find_thread (#{@find_thread}) still alive." if $DEBUG
279                         true
280                     else
281                         puts "@find_thread (#{@find_thread}) asleep now." if $DEBUG
282                         #Not really async now.
283                         get_images_async
284                         false #continue == false if you get to here. Stop timeout_add.
285                     end
286                 else
287                     # Stop if the book find thread has stopped.
288                     @find_thread.alive?
289                 end
290                 # continue == false if @find_error OR if results are returned
291                 # @new_book_dialog.sensitive is a bad call if window has been destroyed
292                 # timeout_add ends if continue is false!
293 
294                 unless continue
295                         unless @find_thread.alive? #This happens after find_thread is done
296                                 unless @destroyed
297                                         @new_book_dialog.sensitive = true
298                                         @button_add.sensitive = false
299                                 end
300                         end
301                 end
302 
303                 continue #timeout_add loop condition
304             end
305         end
306 
307         def decode_cuecat?(entry)
308             if entry.text=~/^\..*?\..*?\.(.*?)\.$/
309                tmp = $1.tr('a-zA-Z0-9+-', ' -_')
310                tmp = ((32 + tmp.length * 3/4).to_i.chr << tmp).unpack('u')[0]
311                tmp.chomp!("\000")
312                entry.text = tmp.gsub!(/./) {|c| (c[0] ^ 67).chr }
313                if entry.text.count('^ -~') > 0
314                    entry.text = 'Bad scan result'
315                end
316            end
317         end
318 
319         def on_results_button_press_event(widget, event)
320             # double left click
321             if event.event_type == Gdk::Event::BUTTON2_PRESS and
322                event.button == 1
323 
324                 on_add
325             end
326         end
327 
328         def on_add
329             return unless @button_add.sensitive?
330             @find_thread.kill if @find_thread
331             @image_thread.kill if @image_thread
332 
333             begin
334                 libraries = Libraries.instance.all_libraries
335                 library, new_library =
336                     @combo_libraries.selection_from_libraries(libraries)
337                 books_to_add = []
338                 if @isbn_radiobutton.active?
339                     # Perform the ISBN search via the providers.
340                     isbn = begin
341                         Library.canonicalise_isbn(@entry_isbn.text)
342                     rescue
343                         raise _("Couldn't validate the EAN/ISBN you " +
344                                 "provided.  Make sure it is written " +
345                                 "correctly, and try again.")
346                     end
347                     assert_not_exist(library, @entry_isbn.text)
348                     books_to_add << Alexandria::BookProviders.isbn_search(isbn)
349                 else
350                     @treeview_results.selection.selected_each do |model, path,
351                                                                  iter|
352                         @results.each do |book, cover|
353                             next unless book.ident == iter[1]
354                             #print iter[0].inspect
355                             #print "  "
356                             #puts iter[1].inspect
357                             begin
358                                 next unless
359                                     assert_not_exist(library, book.isbn)
360                             rescue Alexandria::Library::InvalidISBNError
361                                 next unless
362                                     KeepBadISBNDialog.new(@parent, book).keep?
363                                 book.isbn = book.saved_ident = nil
364                             rescue Alexandria::Library::NoISBNError
365                                 book.isbn = book.saved_ident = nil
366                                 books_to_add << [book, cover]
367                                 next
368                             end
369                             books_to_add << [book, cover]
370 
371                         end
372                     end
373                 end
374 
375                 # Save the books in the library.
376                 books_to_add.each do |book, cover_uri|
377                     unless cover_uri.nil?
378                         library.save_cover(book, cover_uri)
379                     end
380                     library << book
381                     library.save(book)
382                 end
383 
384                 # Do not destroy if there is no addition.
385                 return if books_to_add.empty?
386 
387                 # Now we can destroy the dialog and go back to the main
388                 # application.
389                 @new_book_dialog.destroy
390                 @block.call(books_to_add.map { |x| x.first },
391                             library,
392                             new_library)
393             rescue => e
394                 ErrorDialog.new(@parent, _("Couldn't add the book"), e.message)
395             end
396         end
397 
398         def on_cancel
399             @find_thread.kill if @find_thread
400             @image_thread.kill if @image_thread
401             @new_book_dialog.destroy
402         end
403 
404         def on_focus
405             if @isbn_radiobutton.active? and @entry_isbn.text.strip.empty?
406                 clipboard = Gtk::Clipboard.get(Gdk::Selection::CLIPBOARD)
407                 if text = clipboard.wait_for_text
408                     @entry_isbn.text = text if
409                         Library.valid_isbn?(text) or Library.valid_ean?(text) or
410                         Library.valid_upc?(text)
411                 end
412             end
413         end
414 
415         def on_clicked(widget, event)
416             if event.event_type == Gdk::Event::BUTTON_PRESS and
417                event.button == 1
418 
419                 radio, target_widget, box2, box3 = case widget
420                     when @eventbox_entry_search
421                         [@title_radiobutton, @entry_search,
422                          @eventbox_combo_search, @eventbox_entry_isbn]
423 
424                     when @eventbox_combo_search
425                         [@title_radiobutton, @combo_search,
426                          @eventbox_entry_search, @eventbox_entry_isbn]
427 
428                     when @eventbox_entry_isbn
429                         [@isbn_radiobutton, @entry_isbn,
430                          @eventbox_entry_search, @eventbox_combo_search]
431                 end
432                 radio.active = true
433                 target_widget.grab_focus
434                 widget.above_child = false
435                 box2.above_child = box3.above_child = true
436             end
437         end
438 
439         def on_help
440             begin
441                 Gnome::Help.display('alexandria', 'add-book-by-isbn')
442             rescue => e
443                 ErrorDialog.new(@preferences_dialog, e.message)
444             end
445         end
446 
447         #######
448         private
449         #######
450 
451         def assert_not_exist(library, isbn)
452             # Check that the book doesn't already exist in the library.
453             canonical = Library.canonicalise_isbn(isbn)
454             if book = library.find { |book| book.isbn == canonical }
455                 raise DuplicateBookException, _("'%s' already exists in '%s' (titled '%s').") % \
456                         [ isbn, library.name, book.title.sub("&", "&amp;") ]
457             end
458             true
459         end
460     end
461 end
462 end

Generated using the rcov code coverage analysis tool for Ruby version 0.8.0.

Valid XHTML 1.0! Valid CSS!