alexandria-book-collection-manager 0.7.1 → 0.7.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (210) hide show
  1. checksums.yaml +5 -5
  2. data/.github/dependabot.yml +9 -0
  3. data/.gitignore +5 -2
  4. data/.hound.yml +2 -0
  5. data/.rubocop.yml +113 -45
  6. data/.rubocop_todo.yml +82 -170
  7. data/.simplecov +5 -1
  8. data/.travis.yml +45 -0
  9. data/.yardopts +1 -1
  10. data/CHANGELOG.md +60 -0
  11. data/ChangeLog.0 +33 -35
  12. data/Gemfile +6 -5
  13. data/INSTALL.md +164 -0
  14. data/README.md +52 -42
  15. data/Rakefile +95 -109
  16. data/TODO.md +9 -1
  17. data/alexandria-book-collection-manager.gemspec +52 -45
  18. data/bin/alexandria +31 -53
  19. data/doc/AUTHORS +61 -0
  20. data/doc/BUGS +31 -0
  21. data/doc/FAQ +365 -0
  22. data/doc/HACKING +19 -0
  23. data/doc/NEWS +341 -0
  24. data/doc/alexandria.1 +120 -0
  25. data/doc/cuecat_support.rdoc +67 -0
  26. data/doc/dependency_decisions.yml +80 -0
  27. data/lib/alexandria.rb +29 -37
  28. data/lib/alexandria/about.rb +52 -51
  29. data/lib/alexandria/book_providers.rb +94 -101
  30. data/lib/alexandria/book_providers/adlibris.rb +45 -85
  31. data/lib/alexandria/book_providers/amazon_aws.rb +105 -113
  32. data/lib/alexandria/book_providers/amazon_ecs_util.rb +293 -324
  33. data/lib/alexandria/book_providers/barnes_and_noble.rb +54 -53
  34. data/lib/alexandria/book_providers/douban.rb +29 -51
  35. data/lib/alexandria/book_providers/proxis.rb +42 -59
  36. data/lib/alexandria/book_providers/pseudomarc.rb +79 -99
  37. data/lib/alexandria/book_providers/siciliano.rb +68 -70
  38. data/lib/alexandria/book_providers/thalia.rb +46 -45
  39. data/lib/alexandria/book_providers/web.rb +17 -33
  40. data/lib/alexandria/book_providers/worldcat.rb +74 -102
  41. data/lib/alexandria/book_providers/z3950.rb +170 -174
  42. data/lib/alexandria/config.rb +5 -3
  43. data/lib/alexandria/console.rb +10 -21
  44. data/lib/alexandria/default_preferences.rb +37 -0
  45. data/lib/alexandria/execution_queue.rb +17 -15
  46. data/lib/alexandria/export_format.rb +47 -0
  47. data/lib/alexandria/export_library.rb +188 -302
  48. data/lib/alexandria/import_library.rb +114 -155
  49. data/lib/alexandria/import_library_csv.rb +46 -96
  50. data/lib/alexandria/library_collection.rb +79 -0
  51. data/lib/alexandria/library_sort_order.rb +45 -0
  52. data/lib/alexandria/library_store.rb +233 -0
  53. data/lib/alexandria/logging.rb +15 -19
  54. data/lib/alexandria/models/book.rb +15 -20
  55. data/lib/alexandria/models/library.rb +81 -363
  56. data/lib/alexandria/net.rb +7 -6
  57. data/lib/alexandria/preferences.rb +73 -91
  58. data/lib/alexandria/scanners.rb +4 -2
  59. data/lib/alexandria/scanners/{cuecat.rb → cue_cat.rb} +24 -20
  60. data/lib/alexandria/scanners/keyboard.rb +10 -8
  61. data/lib/alexandria/smart_library.rb +135 -171
  62. data/lib/alexandria/ui.rb +17 -15
  63. data/lib/alexandria/ui/about_dialog.rb +49 -0
  64. data/lib/alexandria/ui/{dialogs/acquire_dialog.rb → acquire_dialog.rb} +129 -152
  65. data/lib/alexandria/ui/alert_dialog.rb +64 -0
  66. data/lib/alexandria/ui/bad_isbns_dialog.rb +41 -0
  67. data/lib/alexandria/ui/{dialogs/barcode_animation.rb → barcode_animation.rb} +18 -15
  68. data/lib/alexandria/ui/{dialogs/book_properties_dialog.rb → book_properties_dialog.rb} +44 -61
  69. data/lib/alexandria/ui/{dialogs/book_properties_dialog_base.rb → book_properties_dialog_base.rb} +84 -89
  70. data/lib/alexandria/ui/builder_base.rb +9 -27
  71. data/lib/alexandria/ui/callbacks.rb +188 -186
  72. data/lib/alexandria/ui/columns.rb +2 -0
  73. data/lib/alexandria/ui/completion_models.rb +12 -23
  74. data/lib/alexandria/ui/confirm_erase_dialog.rb +33 -0
  75. data/lib/alexandria/ui/conflict_while_copying_dialog.rb +34 -0
  76. data/lib/alexandria/ui/dndable.rb +10 -8
  77. data/lib/alexandria/ui/error_dialog.rb +25 -0
  78. data/lib/alexandria/ui/export_dialog.rb +139 -0
  79. data/lib/alexandria/ui/icons.rb +49 -65
  80. data/lib/alexandria/ui/iconview.rb +15 -13
  81. data/lib/alexandria/ui/iconview_tooltips.rb +43 -58
  82. data/lib/alexandria/ui/import_dialog.rb +157 -0
  83. data/lib/alexandria/ui/init.rb +23 -33
  84. data/lib/alexandria/ui/keep_bad_isbn_dialog.rb +36 -0
  85. data/lib/alexandria/ui/libraries_combo.rb +18 -14
  86. data/lib/alexandria/ui/listview.rb +77 -88
  87. data/lib/alexandria/ui/main_app.rb +26 -26
  88. data/lib/alexandria/ui/misc_dialogs.rb +10 -0
  89. data/lib/alexandria/ui/multi_drag_treeview.rb +30 -41
  90. data/lib/alexandria/ui/{dialogs/new_book_dialog.rb → new_book_dialog.rb} +168 -215
  91. data/lib/alexandria/ui/new_book_dialog_manual.rb +139 -0
  92. data/lib/alexandria/ui/new_provider_dialog.rb +100 -0
  93. data/lib/alexandria/ui/new_smart_library_dialog.rb +74 -0
  94. data/lib/alexandria/ui/preferences_dialog.rb +313 -0
  95. data/lib/alexandria/ui/provider_preferences_base_dialog.rb +95 -0
  96. data/lib/alexandria/ui/provider_preferences_dialog.rb +35 -0
  97. data/lib/alexandria/ui/really_delete_dialog.rb +53 -0
  98. data/lib/alexandria/ui/{sidepane.rb → sidepane_manager.rb} +62 -72
  99. data/lib/alexandria/ui/skip_entry_dialog.rb +33 -0
  100. data/lib/alexandria/ui/smart_library_properties_dialog.rb +60 -0
  101. data/lib/alexandria/ui/{dialogs/smart_library_properties_dialog_base.rb → smart_library_properties_dialog_base.rb} +96 -172
  102. data/lib/alexandria/ui/smart_library_rule_box.rb +119 -0
  103. data/lib/alexandria/ui/sound.rb +13 -13
  104. data/lib/alexandria/ui/ui_manager.rb +262 -283
  105. data/lib/alexandria/undo_manager.rb +3 -0
  106. data/lib/alexandria/version.rb +6 -19
  107. data/lib/alexandria/web_themes.rb +24 -21
  108. data/po/Makefile +2 -2
  109. data/po/cs.po +993 -880
  110. data/po/cy.po +957 -874
  111. data/po/de.po +990 -869
  112. data/po/el.po +989 -869
  113. data/po/es.po +985 -865
  114. data/po/fr.po +986 -870
  115. data/po/ga.po +907 -823
  116. data/po/gl.po +981 -865
  117. data/po/it.po +986 -868
  118. data/po/ja.po +969 -853
  119. data/po/mk.po +983 -863
  120. data/po/nb.po +979 -863
  121. data/po/nl.po +983 -864
  122. data/po/pl.po +1020 -969
  123. data/po/pt.po +988 -861
  124. data/po/pt_BR.po +984 -868
  125. data/po/ru.po +992 -873
  126. data/po/sk.po +987 -869
  127. data/po/sv.po +977 -861
  128. data/po/uk.po +975 -865
  129. data/po/zh_TW.po +976 -860
  130. data/schemas/alexandria.schemas +25 -3
  131. data/share/alexandria/glade/acquire_dialog__builder.glade +15 -12
  132. data/share/alexandria/glade/book_properties_dialog__builder.glade +171 -299
  133. data/share/alexandria/glade/main_app__builder.glade +24 -33
  134. data/share/alexandria/glade/new_book_dialog__builder.glade +27 -59
  135. data/share/alexandria/glade/preferences_dialog__builder.glade +250 -290
  136. data/share/gnome/help/alexandria/C/introduction.xml +0 -8
  137. data/share/gnome/help/alexandria/C/searching.xml +1 -1
  138. data/share/gnome/help/alexandria/C/smart-libraries.xml +2 -2
  139. data/share/gnome/help/alexandria/C/working-with-libraries.xml +1 -1
  140. data/share/gnome/help/alexandria/fr/alexandria.xml +1 -1
  141. data/share/gnome/help/alexandria/ja/introduction.xml +0 -8
  142. data/share/gnome/help/alexandria/ja/smart-libraries.xml +1 -1
  143. data/spec/alexandria/book_providers/world_cat_provider_spec.rb +160 -0
  144. data/spec/alexandria/book_providers_spec.rb +77 -210
  145. data/spec/alexandria/book_spec.rb +16 -12
  146. data/spec/alexandria/console_spec.rb +27 -0
  147. data/spec/alexandria/export_library_spec.rb +130 -0
  148. data/spec/alexandria/library_spec.rb +130 -172
  149. data/spec/alexandria/library_store_spec.rb +37 -0
  150. data/spec/alexandria/preferences_spec.rb +46 -17
  151. data/spec/alexandria/scanners/cue_cat_spec.rb +52 -0
  152. data/spec/alexandria/smart_library_spec.rb +32 -25
  153. data/spec/alexandria/ui/about_dialog_spec.rb +14 -0
  154. data/spec/alexandria/ui/acquire_dialog_spec.rb +14 -0
  155. data/spec/alexandria/ui/alert_dialog_spec.rb +16 -0
  156. data/spec/alexandria/ui/bad_isbns_dialog_spec.rb +14 -0
  157. data/spec/alexandria/ui/book_properties_dialog_spec.rb +17 -0
  158. data/spec/alexandria/ui/confirm_erase_dialog_spec.rb +14 -0
  159. data/spec/alexandria/ui/conflict_while_copying_dialog_spec.rb +16 -0
  160. data/spec/alexandria/ui/error_dialog_spec.rb +14 -0
  161. data/spec/alexandria/ui/export_dialog_spec.rb +15 -0
  162. data/spec/alexandria/ui/icons_spec.rb +26 -0
  163. data/spec/alexandria/ui/iconview_spec.rb +9 -21
  164. data/spec/alexandria/ui/import_dialog_spec.rb +41 -0
  165. data/spec/alexandria/ui/keep_bad_isbn_dialog_spec.rb +17 -0
  166. data/spec/alexandria/ui/main_app_spec.rb +8 -33
  167. data/spec/alexandria/ui/new_book_dialog_manual_spec.rb +15 -0
  168. data/spec/alexandria/ui/new_book_dialog_spec.rb +22 -0
  169. data/spec/alexandria/ui/new_provider_dialog_spec.rb +30 -0
  170. data/spec/alexandria/ui/new_smart_library_dialog_spec.rb +39 -0
  171. data/spec/alexandria/ui/preferences_dialog_spec.rb +14 -0
  172. data/spec/alexandria/ui/provider_preferences_dialog_spec.rb +34 -0
  173. data/spec/alexandria/ui/really_delete_dialog_spec.rb +16 -0
  174. data/spec/alexandria/ui/sidepane_manager_spec.rb +15 -0
  175. data/spec/alexandria/ui/skip_entry_dialog_spec.rb +14 -0
  176. data/spec/alexandria/ui/smart_library_properties_dialog_spec.rb +32 -0
  177. data/spec/alexandria/ui/sound_spec.rb +4 -2
  178. data/spec/alexandria/ui/ui_manager_spec.rb +45 -20
  179. data/spec/end_to_end/basic_run_spec.rb +57 -0
  180. data/spec/spec_helper.rb +66 -33
  181. data/tasks/setup.rb +5 -3
  182. data/tasks/spec.rake +18 -3
  183. data/util/rake/fileinstall.rb +38 -40
  184. data/util/rake/gettextgenerate.rb +15 -70
  185. data/util/rake/omfgenerate.rb +10 -10
  186. metadata +176 -60
  187. data/INSTALL.rdoc +0 -148
  188. data/dogtail/basic_run_test.py +0 -9
  189. data/lib/alexandria/book_providers/bol_it.rb +0 -160
  190. data/lib/alexandria/book_providers/deastore.rb +0 -273
  191. data/lib/alexandria/book_providers/ibs_it.rb +0 -147
  192. data/lib/alexandria/book_providers/mcu.rb +0 -169
  193. data/lib/alexandria/book_providers/renaud.rb +0 -140
  194. data/lib/alexandria/book_providers/webster_it.rb +0 -167
  195. data/lib/alexandria/ui/dialogs/about_dialog.rb +0 -59
  196. data/lib/alexandria/ui/dialogs/alert_dialog.rb +0 -70
  197. data/lib/alexandria/ui/dialogs/bad_isbns_dialog.rb +0 -43
  198. data/lib/alexandria/ui/dialogs/export_dialog.rb +0 -171
  199. data/lib/alexandria/ui/dialogs/import_dialog.rb +0 -196
  200. data/lib/alexandria/ui/dialogs/misc_dialogs.rb +0 -85
  201. data/lib/alexandria/ui/dialogs/new_book_dialog_manual.rb +0 -154
  202. data/lib/alexandria/ui/dialogs/new_smart_library_dialog.rb +0 -74
  203. data/lib/alexandria/ui/dialogs/preferences_dialog.rb +0 -578
  204. data/lib/alexandria/ui/dialogs/smart_library_properties_dialog.rb +0 -57
  205. data/spec/alexandria/scanners/cuecat_spec.rb +0 -65
  206. data/spec/alexandria/ui/dialogs_spec.rb +0 -94
  207. data/spec/alexandria/ui/sidepane_spec.rb +0 -27
  208. data/spec/alexandria/ui/ui_utilities_spec.rb +0 -60
  209. data/spec/alexandria/utilities_spec.rb +0 -50
  210. data/tasks/dogtail.rake +0 -4
@@ -1,8 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Alexandria
2
4
  module Config
3
- SHARE_DIR = File.expand_path('../../share', File.dirname(__FILE__))
4
- SOUNDS_DIR = "#{SHARE_DIR}/sounds/alexandria".freeze
5
- DATA_DIR = "#{SHARE_DIR}/alexandria".freeze
5
+ SHARE_DIR = File.expand_path("../../share", File.dirname(__FILE__))
6
+ SOUNDS_DIR = "#{SHARE_DIR}/sounds/alexandria"
7
+ DATA_DIR = "#{SHARE_DIR}/alexandria"
6
8
  MAIN_DATA_DIR = DATA_DIR
7
9
  end
8
10
  end
@@ -1,30 +1,19 @@
1
- # Copyright (C) 2008 Joseph Method
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.
1
+ # frozen_string_literal: true
2
+
3
+ # This file is part of Alexandria.
12
4
  #
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., 51 Franklin Street,
16
- # Fifth Floor, Boston, MA 02110-1301 USA.
5
+ # See the file README.md for authorship and licensing information.
17
6
 
18
7
  module Alexandria
19
8
  def self.list_books_on_console(_title = true, authors = true)
20
- libraries_simpleton = Alexandria::Libraries.instance
21
- libraries_simpleton.reload
22
- libraries = Alexandria::Library.loadall
23
- output_string = ''
9
+ collection = Alexandria::LibraryCollection.instance
10
+ collection.reload
11
+ libraries = collection.all_regular_libraries
12
+ output_string = ""
24
13
  @books = libraries.flatten
25
14
  @books.each do |book|
26
- book_authors = book.authors.join(' & ') if authors
27
- output_string += [book.title, book_authors].join(', ') + "\n"
15
+ book_authors = book.authors.join(" & ") if authors
16
+ output_string += [book.title, book_authors].join(", ") + "\n"
28
17
  end
29
18
  output_string
30
19
  end
@@ -0,0 +1,37 @@
1
+ # This file is automatically generated by the alexandria installer.
2
+ # Do not edit it directly.
3
+
4
+ module Alexandria
5
+ class Preferences
6
+ DEFAULT_VALUES = {
7
+ "position" => [0, 0],
8
+ "size" => [640, 480],
9
+ "maximized" => false,
10
+ "sidepane_position" => 140,
11
+ "view_as" => 0,
12
+ "arrange_icons_mode" => 0,
13
+ "reverse_icons" => false,
14
+ "selected_library" => "",
15
+ "barcode_scanner" => "CueCat",
16
+ "play_scanning_sound" => true,
17
+ "play_scan_sound" => true,
18
+ "toolbar_visible" => true,
19
+ "sidepane_visible" => true,
20
+ "statusbar_visible" => true,
21
+ "col_authors_visible" => true,
22
+ "col_edition_visible" => true,
23
+ "col_isbn_visible" => true,
24
+ "col_publisher_visible" => true,
25
+ "col_publish_date_visible" => true,
26
+ "col_loaned_to_visible" => true,
27
+ "col_rating_visible" => true,
28
+ "col_redd_visible" => true,
29
+ "col_own_visible" => true,
30
+ "col_want_visible" => true,
31
+ "col_tags_visible" => true,
32
+ "cols_width" => "{}",
33
+ "providers_priority" => ["Amazon", "BarnesAndNoble", "AdLibris", "Proxis", "Thalia", "Siciliano", "WorldCat", "LOC", "BL", "SBN"],
34
+ "view_advanced_settings" => false
35
+ }
36
+ end
37
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copyright (C) 2005-2006 Laurent Sansonetti
2
4
  #
3
5
  # Alexandria is free software; you can redistribute it and/or
@@ -15,8 +17,6 @@
15
17
  # write to the Free Software Foundation, Inc., 51 Franklin Street,
16
18
  # Fifth Floor, Boston, MA 02110-1301 USA.
17
19
 
18
- require 'thread'
19
-
20
20
  # Provides a way for two threads to communicate via Proc objects.
21
21
  #
22
22
  # Thread A can request calls, providing a Proc object and runtime arguments,
@@ -41,7 +41,7 @@ module Alexandria
41
41
 
42
42
  def self.current
43
43
  @@current_queue
44
- rescue
44
+ rescue StandardError
45
45
  nil
46
46
  end
47
47
 
@@ -60,12 +60,14 @@ module Alexandria
60
60
  break @pending_calls.pop
61
61
  end
62
62
  return if ary.nil?
63
+
63
64
  id, procedure, args, need_retval = ary
64
65
  retval = procedure.call(*args)
65
- if need_retval
66
- @protect_pending_retvals.synchronize do
67
- @pending_retvals << [id, retval]
68
- end
66
+
67
+ return unless need_retval
68
+
69
+ @protect_pending_retvals.synchronize do
70
+ @pending_retvals << [id, retval]
69
71
  end
70
72
  end
71
73
 
@@ -80,14 +82,14 @@ module Alexandria
80
82
  @id += 1
81
83
  @pending_calls << [@id, procedure, args, need_retval]
82
84
  end
83
- if need_retval
84
- loop do
85
- @protect_pending_retvals.synchronize do
86
- ary = @pending_retvals.find { |id, _retval| id == @id }
87
- if ary
88
- @pending_retvals.delete(ary)
89
- return ary[1]
90
- end
85
+ return unless need_retval
86
+
87
+ loop do
88
+ @protect_pending_retvals.synchronize do
89
+ ary = @pending_retvals.find { |id, _retval| id == @id }
90
+ if ary
91
+ @pending_retvals.delete(ary)
92
+ return ary[1]
91
93
  end
92
94
  end
93
95
  end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file is part of Alexandria.
4
+ #
5
+ # See the file README.md for authorship and licensing information.
6
+
7
+ module Alexandria
8
+ class ExportFormat
9
+ attr_reader :name, :ext, :message
10
+
11
+ include GetText
12
+ include Logging
13
+ extend GetText
14
+ bindtextdomain(Alexandria::TEXTDOMAIN, charset: "UTF-8")
15
+
16
+ def self.all
17
+ [
18
+ new(_("Archived ONIX XML"), "onix.tbz2", :export_as_onix_xml_archive),
19
+ new(_("Archived Tellico XML"), "tc", :export_as_tellico_xml_archive),
20
+ new(_("BibTeX"), "bib", :export_as_bibtex),
21
+ new(_("CSV list"), "csv", :export_as_csv_list),
22
+ new(_("ISBN List"), "txt", :export_as_isbn_list),
23
+ new(_("iPod Notes"), nil, :export_as_ipod_notes),
24
+ new(_("HTML Web Page"), nil, :export_as_html, true)
25
+ ]
26
+ end
27
+
28
+ def invoke(library, sort_order, filename, *args)
29
+ sorted = ExportLibrary.new(library, sort_order)
30
+ log.debug { "Exporting library sorted by #{sort_order}" }
31
+ sorted.send(@message, filename, *args)
32
+ end
33
+
34
+ def needs_preview?
35
+ @needs_preview
36
+ end
37
+
38
+ private
39
+
40
+ def initialize(name, ext, message, needs_preview = false)
41
+ @name = name
42
+ @ext = ext
43
+ @message = message
44
+ @needs_preview = needs_preview
45
+ end
46
+ end
47
+ end
@@ -1,75 +1,18 @@
1
- # Copyright (C) 2004-2006 Laurent Sansonetti
2
- # Copyright (C) 2007 Cathal Mc Ginley
3
- # Copyright (C) 2014, 2016 Matijs van Zuijlen
4
- #
5
- # Alexandria is free software; you can redistribute it and/or
6
- # modify it under the terms of the GNU General Public License as
7
- # published by the Free Software Foundation; either version 2 of the
8
- # License, or (at your option) any later version.
9
- #
10
- # Alexandria is distributed in the hope that it will be useful,
11
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
- # General Public License for more details.
14
- #
15
- # You should have received a copy of the GNU General Public
16
- # License along with Alexandria; see the file COPYING. If not,
17
- # write to the Free Software Foundation, Inc., 51 Franklin Street,
18
- # Fifth Floor, Boston, MA 02110-1301 USA.
1
+ # frozen_string_literal: true
19
2
 
20
- # Export sorting added 23 Oct 2007 by Cathal Mc Ginley
21
- # Classes LibrarySortOrder and SortedLibrary, and changed ExportFormat#invoke
22
- # iPod Notes support added 20 January 2008 by Tim Malone
23
- # require 'cgi'
3
+ # This file is part of Alexandria.
4
+ #
5
+ # See the file README.md for authorship and licensing information.
24
6
 
25
- require 'csv'
26
- require 'image_size'
7
+ require "csv"
8
+ require "image_size"
9
+ require "tmpdir"
27
10
 
28
11
  module Alexandria
29
- class LibrarySortOrder
30
- include Logging
31
-
32
- def initialize(book_attribute, ascending = true)
33
- @book_attribute = book_attribute
34
- @ascending = ascending
35
- end
36
-
37
- def sort(library)
38
- sorted = library.sort_by do |book|
39
- book.send(@book_attribute)
40
- end
41
- sorted.reverse! unless @ascending
42
- sorted
43
- rescue => ex
44
- log.warn { "Could not sort library by #{@book_attribute.inspect}: #{ex.message}" }
45
- library
46
- end
47
-
48
- def to_s
49
- "#{@book_attribute} #{@ascending ? '(ascending)' : '(descending)'}"
50
- end
51
-
52
- class Unsorted < LibrarySortOrder
53
- def initialize; end
54
-
55
- def sort(library)
56
- library
57
- end
58
-
59
- def to_s
60
- 'default order'
61
- end
62
- end
63
- end
64
-
65
- class SortedLibrary < Library
12
+ class ExportLibrary
66
13
  def initialize(library, sort_order)
67
- super(library.name)
68
14
  @library = library
69
- sorted = sort_order.sort(library)
70
- sorted.each do |book|
71
- self << book
72
- end
15
+ @sorted = sort_order.sort(library)
73
16
  end
74
17
 
75
18
  def cover(book)
@@ -83,89 +26,45 @@ module Alexandria
83
26
  def copy_covers(dest)
84
27
  @library.copy_covers(dest)
85
28
  end
86
- end
87
-
88
- class ExportFormat
89
- attr_reader :name, :ext, :message
90
-
91
- include GetText
92
- include Logging
93
- extend GetText
94
- bindtextdomain(Alexandria::TEXTDOMAIN, charset: 'UTF-8')
95
-
96
- def self.all
97
- [
98
- new(_('Archived ONIX XML'), 'onix.tbz2', :export_as_onix_xml_archive),
99
- new(_('Archived Tellico XML'), 'tc', :export_as_tellico_xml_archive),
100
- new(_('BibTeX'), 'bib', :export_as_bibtex),
101
- new(_('CSV list'), 'csv', :export_as_csv_list),
102
- new(_('ISBN List'), 'txt', :export_as_isbn_list),
103
- new(_('iPod Notes'), nil, :export_as_ipod_notes),
104
- new(_('HTML Web Page'), nil, :export_as_html, true)
105
- ]
106
- end
107
29
 
108
- def invoke(library, sort_order, filename, *args)
109
- if sort_order
110
- sorted = SortedLibrary.new(library, sort_order)
111
- log.debug { "Exporting library sorted by #{sort_order}" }
112
- sorted.send(@message, filename, *args)
113
- else
114
- library.send(@message, filename, *args)
115
- end
116
- end
117
-
118
- def needs_preview?
119
- @needs_preview
30
+ def name
31
+ @library.name
120
32
  end
121
33
 
122
- private
123
-
124
- def initialize(name, ext, message, needs_preview = false)
125
- @name = name
126
- @ext = ext
127
- @message = message
128
- @needs_preview = needs_preview
34
+ def each(&block)
35
+ @sorted.each(&block)
129
36
  end
130
- end
131
37
 
132
- module Exportable
133
38
  def export_as_onix_xml_archive(filename)
134
- File.open(File.join(Dir.tmpdir, 'onix.xml'), 'w') do |io|
39
+ File.open(File.join(Dir.tmpdir, "onix.xml"), "w") do |io|
135
40
  to_onix_document.write(io, 0)
136
41
  end
137
- copy_covers(File.join(Dir.tmpdir, 'images'))
42
+ copy_covers(File.join(Dir.tmpdir, "images"))
138
43
  Dir.chdir(Dir.tmpdir) do
139
44
  output = `tar -cjf \"#{filename}\" onix.xml images 2>&1`
140
45
  raise output unless $CHILD_STATUS.success?
141
46
  end
142
- FileUtils.rm_rf(File.join(Dir.tmpdir, 'images'))
143
- FileUtils.rm(File.join(Dir.tmpdir, 'onix.xml'))
47
+ FileUtils.rm_rf(File.join(Dir.tmpdir, "images"))
48
+ FileUtils.rm(File.join(Dir.tmpdir, "onix.xml"))
144
49
  end
145
50
 
146
51
  def export_as_tellico_xml_archive(filename)
147
- File.open(File.join(Dir.tmpdir, 'tellico.xml'), 'w') do |io|
148
- begin
149
- to_tellico_document.write(io, 0)
150
- rescue => ex
151
- puts ex.message
152
- puts ex.backtrace
153
- raise ex
154
- end
52
+ File.open(File.join(Dir.tmpdir, "tellico.xml"), "w") do |io|
53
+ to_tellico_document.write(io, 0)
155
54
  end
156
- copy_covers(File.join(Dir.tmpdir, 'images'))
55
+ copy_covers(File.join(Dir.tmpdir, "images"))
157
56
  Dir.chdir(Dir.tmpdir) do
158
57
  output = `zip -q -r \"#{filename}\" tellico.xml images 2>&1`
159
58
  raise output unless $CHILD_STATUS.success?
160
59
  end
161
- FileUtils.rm_rf(File.join(Dir.tmpdir, 'images'))
162
- FileUtils.rm(File.join(Dir.tmpdir, 'tellico.xml'))
60
+ FileUtils.rm_rf(File.join(Dir.tmpdir, "images"))
61
+ FileUtils.rm(File.join(Dir.tmpdir, "tellico.xml"))
163
62
  end
164
63
 
165
64
  def export_as_isbn_list(filename)
166
- File.open(filename, 'w') do |io|
65
+ File.open(filename, "w") do |io|
167
66
  each do |book|
168
- io.puts((book.isbn || ''))
67
+ io.puts((book.isbn || ""))
169
68
  end
170
69
  end
171
70
  end
@@ -173,19 +72,17 @@ module Alexandria
173
72
  def export_as_html(filename, theme)
174
73
  FileUtils.mkdir(filename) unless File.exist?(filename)
175
74
  Dir.chdir(filename) do
176
- copy_covers('pixmaps')
177
- if theme.has_pixmaps?
178
- FileUtils.cp_r(theme.pixmaps_directory, 'pixmaps')
179
- end
180
- FileUtils.cp(theme.css_file, '.')
181
- File.open('index.html', 'w') do |io|
75
+ copy_covers("pixmaps")
76
+ FileUtils.cp_r(theme.pixmaps_directory, "pixmaps") if theme.has_pixmaps?
77
+ FileUtils.cp(theme.css_file, ".")
78
+ File.open("index.html", "w") do |io|
182
79
  io << to_xhtml(File.basename(theme.css_file))
183
80
  end
184
81
  end
185
82
  end
186
83
 
187
84
  def export_as_bibtex(filename)
188
- File.open(filename, 'w') do |io|
85
+ File.open(filename, "w") do |io|
189
86
  io << to_bibtex
190
87
  end
191
88
  end
@@ -194,27 +91,28 @@ module Alexandria
194
91
  FileUtils.mkdir(filename) unless File.exist?(filename)
195
92
  tempdir = Dir.getwd
196
93
  Dir.chdir(filename)
197
- copy_covers('pixmaps')
198
- File.open('index.linx', 'w') do |io|
199
- io.puts '<TITLE>' + name + '</TITLE>'
94
+ copy_covers("pixmaps")
95
+ File.open("index.linx", "w") do |io|
96
+ io.puts "<TITLE>" + name + "</TITLE>"
200
97
  each do |book|
201
- io.puts '<A HREF="' + book.ident + '">' + book.title + '</A>'
98
+ io.puts '<A HREF="' + book.ident + '">' + book.title + "</A>"
202
99
  end
203
100
  io.close
204
101
  end
205
102
  each do |book|
206
- File.open(book.ident, 'w') do |io|
103
+ File.open(book.ident, "w") do |io|
207
104
  io.puts "<TITLE>#{book.title} </TITLE>"
208
105
  # put a link to the book's cover. only works on iPod 5G and above(?).
209
106
  if File.exist?(cover(book))
210
- io.puts '<A HREF="pixmaps/' + book.ident + '.jpg' + '">' + book.title + '</A>'
107
+ io.puts '<A HREF="pixmaps/' + book.ident + ".jpg" + '">' + book.title + "</A>"
211
108
  else
212
109
  io.puts book.title
213
110
  end
214
- io.puts book.authors.join(', ')
111
+ io.puts book.authors.join(", ")
215
112
  io.puts book.edition
216
- io.puts((book.isbn || ''))
217
- # we need to close the files so the iPod can be ejected/unmounted without us closing Alexandria
113
+ io.puts((book.isbn || ""))
114
+ # we need to close the files so the iPod can be ejected/unmounted
115
+ # without us closing Alexandria
218
116
  io.close
219
117
  end
220
118
  end
@@ -223,85 +121,83 @@ module Alexandria
223
121
  end
224
122
 
225
123
  def export_as_csv_list(filename)
226
- CSV.open(filename, 'w', col_sep: ';') do |csv|
227
- csv << ['Title', 'Authors', 'Publisher', 'Edition', 'ISBN', 'Year Published',
228
- "Rating(#{Book::DEFAULT_RATING} to #{Book::MAX_RATING_STARS})", 'Notes',
229
- 'Want?', 'Read?', 'Own?', 'Tags']
124
+ CSV.open(filename, "w", col_sep: ";") do |csv|
125
+ csv << ["Title", "Authors", "Publisher", "Edition", "ISBN", "Year Published",
126
+ "Rating(#{Book::DEFAULT_RATING} to #{Book::MAX_RATING_STARS})", "Notes",
127
+ "Want?", "Read?", "Own?", "Tags"]
230
128
  each do |book|
231
- csv << [book.title, book.authors.join(', '), book.publisher, book.edition, book.isbn,
232
- book.publishing_year, book.rating, book.notes,
233
- (book.want ? '1' : '0'), (book.redd ? '1' : '0'), (book.own ? '1' : '0'),
234
- (book.tags ? book.tags.join(', ') : '')]
129
+ csv << [book.title, book.authors.join(", "), book.publisher, book.edition,
130
+ book.isbn, book.publishing_year, book.rating, book.notes,
131
+ (book.want ? "1" : "0"), (book.redd ? "1" : "0"), (book.own ? "1" : "0"),
132
+ (book.tags ? book.tags.join(", ") : "")]
235
133
  end
236
134
  end
237
135
  end
238
136
 
239
137
  private
240
138
 
241
- ONIX_DTD_URL = 'http://www.editeur.org/onix/2.1/reference/onix-international.dtd'.freeze
139
+ ONIX_DTD_URL = "http://www.editeur.org/onix/2.1/reference/onix-international.dtd"
242
140
  def to_onix_document
243
141
  doc = REXML::Document.new
244
142
  doc << REXML::XMLDecl.new
245
- doc << REXML::DocType.new('ONIXMessage',
143
+ doc << REXML::DocType.new("ONIXMessage",
246
144
  "SYSTEM \"#{ONIX_DTD_URL}\"")
247
- msg = doc.add_element('ONIXMessage')
248
- header = msg.add_element('Header')
249
- header.add_element('FromCompany').text = 'Alexandria'
250
- header.add_element('FromPerson').text = Etc.getlogin
145
+ msg = doc.add_element("ONIXMessage")
146
+ header = msg.add_element("Header")
147
+ header.add_element("FromCompany").text = "Alexandria"
148
+ header.add_element("FromPerson").text = Etc.getlogin
251
149
  now = Time.now
252
- header.add_element('SentDate').text = '%.4d%.2d%.2d%.2d%.2d' % [
253
- now.year, now.month, now.day, now.hour, now.min
254
- ]
255
- header.add_element('MessageNote').text = name
256
- each_with_index do |book, idx|
150
+ header.add_element("SentDate").text = now.strftime("%Y%m%d%H%M")
151
+ header.add_element("MessageNote").text = name
152
+ @sorted.each_with_index do |book, idx|
257
153
  # fields that are missing: edition and rating.
258
- prod = msg.add_element('Product')
259
- prod.add_element('RecordReference').text = idx
260
- prod.add_element('NotificationType').text = '03' # confirmed
261
- prod.add_element('RecordSourceName').text =
262
- 'Alexandria ' + Alexandria::DISPLAY_VERSION
263
- prod.add_element('ISBN').text = (book.isbn || '')
264
- prod.add_element('ProductForm').text = 'BA' # book
265
- prod.add_element('DistinctiveTitle').text = book.title
154
+ prod = msg.add_element("Product")
155
+ prod.add_element("RecordReference").text = idx
156
+ prod.add_element("NotificationType").text = "03" # confirmed
157
+ prod.add_element("RecordSourceName").text =
158
+ "Alexandria " + Alexandria::DISPLAY_VERSION
159
+ prod.add_element("ISBN").text = (book.isbn || "")
160
+ prod.add_element("ProductForm").text = "BA" # book
161
+ prod.add_element("DistinctiveTitle").text = book.title
266
162
  unless book.authors.empty?
267
163
  book.authors.each do |author|
268
- elem = prod.add_element('Contributor')
164
+ elem = prod.add_element("Contributor")
269
165
  # author
270
- elem.add_element('ContributorRole').text = 'A01'
271
- elem.add_element('PersonName').text = author
166
+ elem.add_element("ContributorRole").text = "A01"
167
+ elem.add_element("PersonName").text = author
272
168
  end
273
169
  end
274
170
  if book.notes && !book.notes.empty?
275
- elem = prod.add_element('OtherText')
171
+ elem = prod.add_element("OtherText")
276
172
  # reader description
277
- elem.add_element('TextTypeCode').text = '12'
278
- elem.add_element('TextFormat').text = '00' # ASCII
279
- elem.add_element('Text').text = book.notes
173
+ elem.add_element("TextTypeCode").text = "12"
174
+ elem.add_element("TextFormat").text = "00" # ASCII
175
+ elem.add_element("Text").text = book.notes
280
176
  end
281
177
  if File.exist?(cover(book))
282
- elem = prod.add_element('MediaFile')
178
+ elem = prod.add_element("MediaFile")
283
179
  # front cover image
284
- elem.add_element('MediaFileTypeCode').text = '04'
285
- elem.add_element('MediaFileFormatCode').text =
286
- (Library.jpeg?(cover(book)) ? '03' : '02')
180
+ elem.add_element("MediaFileTypeCode").text = "04"
181
+ elem.add_element("MediaFileFormatCode").text =
182
+ (Library.jpeg?(cover(book)) ? "03" : "02")
287
183
  # filename
288
- elem.add_element('MediaFileLinkTypeCode').text = '06'
289
- elem.add_element('MediaFileLink').text =
290
- File.join('images', final_cover(book))
184
+ elem.add_element("MediaFileLinkTypeCode").text = "06"
185
+ elem.add_element("MediaFileLink").text =
186
+ File.join("images", final_cover(book))
291
187
  end
292
188
  if book.isbn
293
- BookProviders.each do |provider|
294
- elem = prod.add_element('ProductWebsite')
295
- elem.add_element('ProductWebsiteDescription').text =
189
+ BookProviders.list.each do |provider|
190
+ elem = prod.add_element("ProductWebsite")
191
+ elem.add_element("ProductWebsiteDescription").text =
296
192
  provider.fullname
297
- elem.add_element('ProductWebsiteLink').text =
193
+ elem.add_element("ProductWebsiteLink").text =
298
194
  provider.url(book)
299
195
  end
300
196
  end
301
- elem = prod.add_element('Publisher')
302
- elem.add_element('PublishingRole').text = '01'
303
- elem.add_element('PublisherName').text = book.publisher
304
- prod.add_element('PublicationDate').text = book.publishing_year
197
+ elem = prod.add_element("Publisher")
198
+ elem.add_element("PublishingRole").text = "01"
199
+ elem.add_element("PublisherName").text = book.publisher
200
+ prod.add_element("PublicationDate").text = book.publishing_year
305
201
  end
306
202
  doc
307
203
  end
@@ -311,51 +207,51 @@ module Alexandria
311
207
  # http://periapsis.org/tellico/doc/hacking.html
312
208
  doc = REXML::Document.new
313
209
  doc << REXML::XMLDecl.new
314
- doc << REXML::DocType.new('tellico', 'PUBLIC "-//Robby Stephenson/DTD Tellico V7.0//EN" "http://periapsis.org/tellico/dtd/v7/tellico.dtd"')
315
- tellico = doc.add_element('tellico')
316
- tellico.add_attribute('syntaxVersion', '7')
317
- tellico.add_namespace('http://periapsis.org/tellico/')
318
- collection = tellico.add_element('collection')
319
- collection.add_attribute('title', name)
320
- collection.add_attribute('type', '2')
321
- fields = collection.add_element('fields')
322
- field1 = fields.add_element('field')
210
+ doc << REXML::DocType.new("tellico",
211
+ 'PUBLIC "-//Robby Stephenson/DTD Tellico V7.0//EN"' \
212
+ ' "http://periapsis.org/tellico/dtd/v7/tellico.dtd"')
213
+ tellico = doc.add_element("tellico")
214
+ tellico.add_attribute("syntaxVersion", "7")
215
+ tellico.add_namespace("http://periapsis.org/tellico/")
216
+ collection = tellico.add_element("collection")
217
+ collection.add_attribute("title", name)
218
+ collection.add_attribute("type", "2")
219
+ fields = collection.add_element("fields")
220
+ field1 = fields.add_element("field")
323
221
  # a field named _default implies adding all default book
324
222
  # collection fields
325
- field1.add_attribute('name', '_default')
326
- images = collection.add_element('images')
327
- each_with_index do |book, idx|
328
- entry = collection.add_element('entry')
223
+ field1.add_attribute("name", "_default")
224
+ images = collection.add_element("images")
225
+ @sorted.each_with_index do |book, idx|
226
+ entry = collection.add_element("entry")
329
227
  new_index = (idx + 1).to_s
330
- entry.add_attribute('id', new_index)
228
+ entry.add_attribute("id", new_index)
331
229
  # translate the binding
332
- entry.add_element('title').text = book.title
333
- entry.add_element('isbn').text = (book.isbn || '')
334
- entry.add_element('pub_year').text = book.publishing_year
335
- entry.add_element('binding').text = book.edition
336
- entry.add_element('publisher').text = book.publisher
230
+ entry.add_element("title").text = book.title
231
+ entry.add_element("isbn").text = (book.isbn || "")
232
+ entry.add_element("pub_year").text = book.publishing_year
233
+ entry.add_element("binding").text = book.edition
234
+ entry.add_element("publisher").text = book.publisher
337
235
  unless book.authors.empty?
338
- authors = entry.add_element('authors')
236
+ authors = entry.add_element("authors")
339
237
  book.authors.each do |author|
340
- authors.add_element('author').text = author
238
+ authors.add_element("author").text = author
341
239
  end
342
240
  end
343
- entry.add_element('read').text = book.redd.to_s if book.redd
344
- entry.add_element('loaned').text = book.loaned.to_s if book.loaned
241
+ entry.add_element("read").text = book.redd.to_s if book.redd
242
+ entry.add_element("loaned").text = book.loaned.to_s if book.loaned
345
243
  unless book.rating == Book::DEFAULT_RATING
346
- entry.add_element('rating').text = book.rating
347
- end
348
- if book.notes && !book.notes.empty?
349
- entry.add_element('comments').text = book.notes
244
+ entry.add_element("rating").text = book.rating
350
245
  end
246
+ entry.add_element("comments").text = book.notes if book.notes && !book.notes.empty?
351
247
  if File.exist?(cover(book))
352
- entry.add_element('cover').text = final_cover(book)
353
- image = images.add_element('image')
354
- image.add_attribute('id', final_cover(book))
248
+ entry.add_element("cover").text = final_cover(book)
249
+ image = images.add_element("image")
250
+ image.add_attribute("id", final_cover(book))
355
251
  image_s = ImageSize.new(IO.read(cover(book)))
356
- image.add_attribute('height', image_s.get_height.to_s)
357
- image.add_attribute('width', image_s.get_width.to_s)
358
- image.add_attribute('format', image_s.get_type)
252
+ image.add_attribute("height", image_s.height.to_s)
253
+ image.add_attribute("width", image_s.width.to_s)
254
+ image.add_attribute("format", image_s.format)
359
255
  end
360
256
  end
361
257
  doc
@@ -364,110 +260,107 @@ module Alexandria
364
260
  def xhtml_escape(str)
365
261
  escaped = str.dup
366
262
  # used to occasionally use CGI.escapeHTML
367
- escaped.gsub!(/&/, '&amp;')
368
- escaped.gsub!(/</, '&lt;')
369
- escaped.gsub!(/>/, '&gt;')
370
- escaped.gsub!(/\"/, '&quot;')
263
+ escaped.gsub!(/&/, "&amp;")
264
+ escaped.gsub!(/</, "&lt;")
265
+ escaped.gsub!(/>/, "&gt;")
266
+ escaped.gsub!(/"/, "&quot;")
371
267
  escaped
372
268
  end
373
269
 
374
270
  def to_xhtml(css)
375
- generator = 'Alexandria ' + Alexandria::DISPLAY_VERSION
376
- xhtml = ''
377
- xhtml << <<EOS
378
- <?xml version="1.0" encoding="UTF-8"?>
379
- <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
380
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
381
- <html>
382
- <head>
383
- <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
384
- <meta name="Author" content="#{Etc.getlogin}"/>
385
- <meta name="Description" content="List of books"/>
386
- <meta name="Keywords" content="books"/>
387
- <meta name="Generator" content="#{xhtml_escape(generator)}"/>
388
- <title>#{xhtml_escape(name)}</title>
389
- <link rel="stylesheet" href="#{xhtml_escape(css)}" type="text/css"/>
390
- </head>
391
- <body>
392
- <h1 class="library_name">#{xhtml_escape(name)}</h1>
393
- EOS
271
+ generator = "Alexandria " + Alexandria::DISPLAY_VERSION
272
+ xhtml = +""
273
+ xhtml << <<~EOS
274
+ <?xml version="1.0" encoding="UTF-8"?>
275
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
276
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
277
+ <html>
278
+ <head>
279
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
280
+ <meta name="Author" content="#{Etc.getlogin}"/>
281
+ <meta name="Description" content="List of books"/>
282
+ <meta name="Keywords" content="books"/>
283
+ <meta name="Generator" content="#{xhtml_escape(generator)}"/>
284
+ <title>#{xhtml_escape(name)}</title>
285
+ <link rel="stylesheet" href="#{xhtml_escape(css)}" type="text/css"/>
286
+ </head>
287
+ <body>
288
+ <h1 class="library_name">#{xhtml_escape(name)}</h1>
289
+ EOS
394
290
 
395
291
  each do |book|
396
- xhtml << <<EOS
397
- <div class="book">
398
- <p class="book_isbn">#{book.isbn}</p>
399
- EOS
292
+ xhtml << <<~EOS
293
+ <div class="book">
294
+ <p class="book_isbn">#{book.isbn}</p>
295
+ EOS
400
296
 
401
297
  if File.exist?(cover(book))
402
- xhtml << <<EOS
403
- <img class="book_cover"
404
- src="#{File.join('pixmaps', final_cover(book))}"
405
- alt="Cover file for '#{xhtml_escape(book.title)}'"
406
- EOS
407
298
  image_s = ImageSize.new(IO.read(cover(book)))
408
- xhtml << <<EOS
409
- height="#{image_s.get_height}" width="#{image_s.get_width}"
410
- EOS
411
- xhtml << <<EOS
412
- />
413
- EOS
299
+ xhtml << <<~EOS
300
+ <img class="book_cover"
301
+ src="#{File.join('pixmaps', final_cover(book))}"
302
+ alt="Cover file for '#{xhtml_escape(book.title)}'"
303
+ height="#{image_s.height}" width="#{image_s.width}"
304
+ />
305
+ EOS
414
306
  else
415
- xhtml << <<EOS
416
- <div class="no_book_cover"></div>
417
- EOS
307
+ xhtml << <<~EOS
308
+ <div class="no_book_cover"></div>
309
+ EOS
418
310
  end
419
311
 
420
312
  unless book.title.nil?
421
- xhtml << <<EOS
422
- <p class="book_title">#{xhtml_escape(book.title)}</p>
423
- EOS
313
+ xhtml << <<~EOS
314
+ <p class="book_title">#{xhtml_escape(book.title)}</p>
315
+ EOS
424
316
  end
425
317
 
426
318
  unless book.authors.empty?
427
319
  xhtml << '<ul class="book_authors">'
428
320
  book.authors.each do |author|
429
- xhtml << <<EOS
430
- <li class="book_author">#{xhtml_escape(author)}</li>
431
- EOS
321
+ xhtml << <<~EOS
322
+ <li class="book_author">#{xhtml_escape(author)}</li>
323
+ EOS
432
324
  end
433
- xhtml << '</ul>'
325
+ xhtml << "</ul>"
434
326
  end
435
327
 
436
328
  unless book.edition.nil?
437
- xhtml << <<EOS
438
- <p class="book_binding">#{xhtml_escape(book.edition)}</p>
439
- EOS
329
+ xhtml << <<~EOS
330
+ <p class="book_binding">#{xhtml_escape(book.edition)}</p>
331
+ EOS
440
332
  end
441
333
 
442
334
  unless book.publisher.nil?
443
- xhtml << <<EOS
444
- <p class="book_publisher">#{xhtml_escape(book.publisher)}</p>
445
- EOS
335
+ xhtml << <<~EOS
336
+ <p class="book_publisher">#{xhtml_escape(book.publisher)}</p>
337
+ EOS
446
338
  end
447
339
 
448
- xhtml << <<EOS
449
- </div>
450
- EOS
340
+ xhtml << <<~EOS
341
+ </div>
342
+ EOS
451
343
  end
452
- xhtml << <<EOS
453
- <p class="copyright">
454
- Generated on #{xhtml_escape(Date.today.to_s)} by <a href="#{xhtml_escape(Alexandria::WEBSITE_URL)}">#{xhtml_escape(generator)}</a>.
455
- </p>
456
- </body>
457
- </html>
458
- EOS
344
+ xhtml << <<~EOS
345
+ <p class="copyright">
346
+ Generated on #{xhtml_escape(Date.today.to_s)}
347
+ by <a href="#{xhtml_escape(Alexandria::WEBSITE_URL)}">#{xhtml_escape(generator)}</a>.
348
+ </p>
349
+ </body>
350
+ </html>
351
+ EOS
459
352
  end
460
353
 
461
354
  def to_bibtex
462
- generator = 'Alexandria ' + Alexandria::DISPLAY_VERSION
463
- bibtex = ''
355
+ generator = "Alexandria " + Alexandria::DISPLAY_VERSION
356
+ bibtex = +""
464
357
  bibtex << "\%Generated on #{Date.today} by: #{generator}\n"
465
358
  bibtex << "\%\n"
466
359
  bibtex << "\n"
467
360
 
468
361
  auths = Hash.new(0)
469
362
  each do |book|
470
- k = (book.authors[0] || 'Anonymous').split[0]
363
+ k = (book.authors[0] || "Anonymous").split[0]
471
364
  if auths.key?(k)
472
365
  auths[k] += 1
473
366
  else
@@ -489,33 +382,26 @@ EOS
489
382
  bibtex << "OPTnote = \"#{latex_escape(book.notes)}\",\n"
490
383
  end
491
384
  # year is a required field in bibtex @BOOK
492
- bibtex << 'year = ' + (book.publishing_year || '"n/a"').to_s + "\n"
385
+ bibtex << "year = " + (book.publishing_year || '"n/a"').to_s + "\n"
493
386
  bibtex << "}\n\n"
494
387
  end
495
388
  bibtex
496
389
  end
497
390
 
498
391
  def latex_escape(str)
499
- return '' if str.nil?
392
+ return "" if str.nil?
393
+
500
394
  my_str = str.dup
501
395
  my_str.gsub!(/%/, '\\%')
502
396
  my_str.gsub!(/~/, '\\textasciitilde')
503
- my_str.gsub!(/\&/, '\\\\&')
397
+ my_str.gsub!(/&/, '\\\\&')
504
398
  my_str.gsub!(/\#/, '\\\\#')
505
399
  my_str.gsub!(/\{/, '\\{')
506
400
  my_str.gsub!(/\}/, '\\}')
507
401
  my_str.gsub!(/_/, '\\_')
508
402
  my_str.gsub!(/\$/, "\\\$")
509
- my_str.gsub!(/\"(.+)\"/, "``\1''")
403
+ my_str.gsub!(/"(.+)"/, "``\1''")
510
404
  my_str
511
405
  end
512
406
  end
513
-
514
- class Library
515
- include Exportable
516
- end
517
-
518
- class SmartLibrary
519
- include Exportable
520
- end
521
407
  end