alexandria-book-collection-manager 0.7.2 → 0.7.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (115) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.hound.yml +2 -0
  4. data/.rubocop.yml +4 -4
  5. data/.rubocop_todo.yml +55 -82
  6. data/CHANGELOG.md +17 -0
  7. data/INSTALL.md +3 -3
  8. data/README.md +1 -2
  9. data/Rakefile +14 -33
  10. data/TODO.md +9 -1
  11. data/alexandria-book-collection-manager.gemspec +4 -3
  12. data/bin/alexandria +21 -34
  13. data/doc/FAQ +2 -5
  14. data/lib/alexandria.rb +4 -16
  15. data/lib/alexandria/book_providers.rb +5 -7
  16. data/lib/alexandria/book_providers/adlibris.rb +5 -3
  17. data/lib/alexandria/book_providers/amazon_aws.rb +3 -1
  18. data/lib/alexandria/book_providers/amazon_ecs_util.rb +8 -0
  19. data/lib/alexandria/book_providers/barnes_and_noble.rb +7 -4
  20. data/lib/alexandria/book_providers/douban.rb +1 -1
  21. data/lib/alexandria/book_providers/proxis.rb +11 -27
  22. data/lib/alexandria/book_providers/renaud.rb +9 -3
  23. data/lib/alexandria/book_providers/siciliano.rb +7 -4
  24. data/lib/alexandria/book_providers/thalia.rb +5 -3
  25. data/lib/alexandria/book_providers/web.rb +11 -29
  26. data/lib/alexandria/book_providers/worldcat.rb +7 -5
  27. data/lib/alexandria/book_providers/z3950.rb +10 -7
  28. data/lib/alexandria/console.rb +5 -18
  29. data/lib/alexandria/execution_queue.rb +2 -1
  30. data/lib/alexandria/export_format.rb +47 -0
  31. data/lib/alexandria/export_library.rb +72 -180
  32. data/lib/alexandria/import_library.rb +14 -23
  33. data/lib/alexandria/import_library_csv.rb +3 -6
  34. data/lib/alexandria/library_collection.rb +78 -0
  35. data/lib/alexandria/library_sort_order.rb +43 -0
  36. data/lib/alexandria/library_store.rb +222 -0
  37. data/lib/alexandria/logging.rb +2 -0
  38. data/lib/alexandria/models/book.rb +8 -16
  39. data/lib/alexandria/models/library.rb +26 -308
  40. data/lib/alexandria/preferences.rb +7 -24
  41. data/lib/alexandria/scanners/cuecat.rb +3 -1
  42. data/lib/alexandria/smart_library.rb +32 -67
  43. data/lib/alexandria/ui/builder_base.rb +6 -26
  44. data/lib/alexandria/ui/callbacks.rb +8 -34
  45. data/lib/alexandria/ui/completion_models.rb +2 -1
  46. data/lib/alexandria/ui/dialogs/about_dialog.rb +35 -47
  47. data/lib/alexandria/ui/dialogs/acquire_dialog.rb +14 -30
  48. data/lib/alexandria/ui/dialogs/alert_dialog.rb +8 -17
  49. data/lib/alexandria/ui/dialogs/bad_isbns_dialog.rb +10 -24
  50. data/lib/alexandria/ui/dialogs/book_properties_dialog.rb +17 -18
  51. data/lib/alexandria/ui/dialogs/book_properties_dialog_base.rb +25 -41
  52. data/lib/alexandria/ui/dialogs/export_dialog.rb +48 -56
  53. data/lib/alexandria/ui/dialogs/import_dialog.rb +31 -51
  54. data/lib/alexandria/ui/dialogs/keep_bad_isbn_dialog.rb +33 -0
  55. data/lib/alexandria/ui/dialogs/misc_dialogs.rb +12 -25
  56. data/lib/alexandria/ui/dialogs/new_book_dialog.rb +57 -94
  57. data/lib/alexandria/ui/dialogs/new_book_dialog_manual.rb +24 -42
  58. data/lib/alexandria/ui/dialogs/new_smart_library_dialog.rb +9 -21
  59. data/lib/alexandria/ui/dialogs/preferences_dialog.rb +27 -32
  60. data/lib/alexandria/ui/dialogs/smart_library_properties_dialog.rb +5 -3
  61. data/lib/alexandria/ui/dialogs/smart_library_properties_dialog_base.rb +15 -12
  62. data/lib/alexandria/ui/icons.rb +11 -22
  63. data/lib/alexandria/ui/init.rb +3 -3
  64. data/lib/alexandria/ui/libraries_combo.rb +1 -0
  65. data/lib/alexandria/ui/listview.rb +5 -21
  66. data/lib/alexandria/ui/multi_drag_treeview.rb +20 -32
  67. data/lib/alexandria/ui/sidepane.rb +9 -24
  68. data/lib/alexandria/ui/ui_manager.rb +36 -60
  69. data/lib/alexandria/undo_manager.rb +1 -0
  70. data/lib/alexandria/version.rb +2 -2
  71. data/lib/alexandria/web_themes.rb +1 -0
  72. data/po/cs.po +0 -4
  73. data/po/cy.po +0 -4
  74. data/po/de.po +0 -4
  75. data/po/el.po +0 -4
  76. data/po/es.po +0 -4
  77. data/po/fr.po +0 -4
  78. data/po/ga.po +0 -4
  79. data/po/gl.po +0 -4
  80. data/po/it.po +0 -4
  81. data/po/ja.po +0 -4
  82. data/po/mk.po +0 -4
  83. data/po/nb.po +0 -4
  84. data/po/nl.po +0 -4
  85. data/po/pl.po +0 -4
  86. data/po/pt.po +0 -4
  87. data/po/pt_BR.po +0 -4
  88. data/po/ru.po +0 -4
  89. data/po/sk.po +0 -4
  90. data/po/sv.po +0 -4
  91. data/po/uk.po +0 -4
  92. data/po/zh_TW.po +0 -4
  93. data/schemas/alexandria.schemas +1 -1
  94. data/share/alexandria/glade/acquire_dialog__builder.glade +14 -11
  95. data/share/alexandria/glade/book_properties_dialog__builder.glade +170 -298
  96. data/share/alexandria/glade/main_app__builder.glade +22 -16
  97. data/share/alexandria/glade/new_book_dialog__builder.glade +26 -58
  98. data/share/alexandria/glade/preferences_dialog__builder.glade +249 -289
  99. data/share/gnome/help/alexandria/C/introduction.xml +0 -4
  100. data/share/gnome/help/alexandria/ja/introduction.xml +0 -4
  101. data/spec/alexandria/book_providers_spec.rb +1 -20
  102. data/spec/alexandria/console_spec.rb +32 -0
  103. data/spec/alexandria/export_library_spec.rb +141 -0
  104. data/spec/alexandria/library_spec.rb +24 -80
  105. data/spec/alexandria/library_store_spec.rb +37 -0
  106. data/spec/alexandria/smart_library_spec.rb +27 -22
  107. data/spec/alexandria/ui/dialogs/new_book_dialog_spec.rb +22 -0
  108. data/spec/alexandria/ui/dialogs_spec.rb +104 -38
  109. data/spec/end_to_end/basic_run_spec.rb +69 -0
  110. data/spec/spec_helper.rb +13 -25
  111. data/tasks/spec.rake +15 -2
  112. data/util/rake/fileinstall.rb +1 -0
  113. metadata +38 -16
  114. data/lib/alexandria/book_providers/deastore.rb +0 -265
  115. data/lib/alexandria/book_providers/mcu.rb +0 -182
@@ -74,7 +74,7 @@ module Alexandria
74
74
  end
75
75
  return results
76
76
  end
77
- rescue
77
+ rescue StandardError
78
78
  raise NoResultsError
79
79
  end
80
80
  end
@@ -87,9 +87,10 @@ module Alexandria
87
87
  private
88
88
 
89
89
  NO_BOOKS_FOUND_REGEXP =
90
- /<strong class="Promotion">Aucun article trouv. selon les crit.res demand.s<\/strong>/
90
+ /<strong class="Promotion">Aucun article trouv. selon les crit.res demand.s<\/strong>/.freeze
91
91
  HYPERLINK_SCAN_REGEXP =
92
- /"(Jeune|Lire)Hyperlien" href.*><strong>([-,'\(\)&\#;\w\s#{ACCENTUATED_CHARS}]*)<\/strong><\/a><br>/
92
+ /"(Jeune|Lire)Hyperlien" href.*><strong>([-,'\(\)&\#;\w\s#{ACCENTUATED_CHARS}]*)<\/strong><\/a><br>/.
93
+ freeze
93
94
 
94
95
  def to_books(data)
95
96
  data = CGI.unescapeHTML(data)
@@ -101,6 +102,7 @@ module Alexandria
101
102
  titles << md[1].strip
102
103
  }
103
104
  raise if titles.empty?
105
+
104
106
  authors = []
105
107
  data.scan(/Nom_Auteur.*><i>([,'.&\#;\w\s#{ACCENTUATED_CHARS}]*)<\/i>/).each { |md|
106
108
  authors2 = []
@@ -110,11 +112,13 @@ module Alexandria
110
112
  authors << authors2
111
113
  }
112
114
  raise if authors.empty?
115
+
113
116
  isbns = []
114
117
  data.scan(/ISBN : ?<\/td><td>(\d+)/).each { |md|
115
118
  isbns << md[0].strip
116
119
  }
117
120
  raise if isbns.empty?
121
+
118
122
  editions = []
119
123
  publish_years = []
120
124
  data.scan(/Parution : <br>(\d{4,}-\d{2,}-\d{2,})/).each { |md|
@@ -122,11 +126,13 @@ module Alexandria
122
126
  publish_years << md[0].strip.split(/-/)[0].to_i
123
127
  }
124
128
  raise if editions.empty? || publish_years.empty?
129
+
125
130
  publishers = []
126
131
  data.scan(/diteur : ([,'.&\#;\w\s#{ACCENTUATED_CHARS}]*)<\/span><br>/).each { |md|
127
132
  publishers << md[0].strip
128
133
  }
129
134
  raise if publishers.empty?
135
+
130
136
  book_covers = []
131
137
  data.scan(/(\/ImagesEditeurs\/[\d]*\/([\dX]*-f.(jpg|gif))
132
138
  |\/francais\/suggestion\/images\/livre\/livre.gif)/x).each { |md|
@@ -90,8 +90,8 @@ module Alexandria
90
90
  private
91
91
 
92
92
  def create_search_uri(search_type, search_term, trying_again = false)
93
- (search_type_code = { SEARCH_BY_ISBN => 'G',
94
- SEARCH_BY_TITLE => 'A',
93
+ (search_type_code = { SEARCH_BY_ISBN => 'G',
94
+ SEARCH_BY_TITLE => 'A',
95
95
  SEARCH_BY_AUTHORS => 'B',
96
96
  SEARCH_BY_KEYWORD => 'X' }[search_type]) || 'X'
97
97
  search_term_encoded = if search_type == SEARCH_BY_ISBN
@@ -148,7 +148,7 @@ module Alexandria
148
148
  result[:url] = "#{SITE}#{slash}#{link_to_description}"
149
149
 
150
150
  book_search_results << result
151
- rescue => ex
151
+ rescue StandardError => ex
152
152
  trace = ex.backtrace.join("\n> ")
153
153
  log.error { "Failed parsing Siciliano search page #{ex.message}\n#{trace}" }
154
154
  end
@@ -163,6 +163,7 @@ module Alexandria
163
163
  # title
164
164
  title_div = doc % 'div#conteudo//div.titulo'
165
165
  raise NoResultsError unless title_div
166
+
166
167
  title_h = title_div % 'h2'
167
168
  title = title_h.inner_text if title_h
168
169
  # title = first_non_empty_text_node(title_div)
@@ -193,6 +194,7 @@ module Alexandria
193
194
  image_urls = []
194
195
  (doc / 'script').each do |script|
195
196
  next if script.children.nil?
197
+
196
198
  script.children.each do |ch|
197
199
  ch_text = ch.to_s
198
200
  if ch_text =~ /ImgSrc\[[\d]\]="(.+)";/
@@ -204,7 +206,7 @@ module Alexandria
204
206
  book = Book.new(title, authors, isbn, publisher, publish_year, binding)
205
207
  result = [book, image_urls.first]
206
208
  result
207
- rescue => ex
209
+ rescue StandardError => ex
208
210
  trace = ex.backtrace.join("\n> ")
209
211
  log.error { "Failed parsing Siciliano product page #{ex.message}\n#{trace}" }
210
212
  nil
@@ -214,6 +216,7 @@ module Alexandria
214
216
  text = ''
215
217
  elem.children.each do |node|
216
218
  next unless node.text?
219
+
217
220
  text = node.to_s.strip
218
221
  break unless text.empty?
219
222
  end
@@ -55,15 +55,16 @@ module Alexandria
55
55
  else
56
56
  results = parse_search_result_data(html_data.body)
57
57
  raise NoResultsError if results.empty?
58
+
58
59
  results.map { |result| get_book_from_search_result(result) }
59
60
  end
60
61
  end
61
62
 
62
63
  def create_search_uri(search_type, search_term)
63
64
  (search_type_code = {
64
- SEARCH_BY_ISBN => 'sq',
65
+ SEARCH_BY_ISBN => 'sq',
65
66
  SEARCH_BY_AUTHORS => 'sa', # Autor
66
- SEARCH_BY_TITLE => 'st', # Titel
67
+ SEARCH_BY_TITLE => 'st', # Titel
67
68
  SEARCH_BY_KEYWORD => 'ssw' # Schlagwort
68
69
  }[search_type]) || ''
69
70
  search_type_code = CGI.escape(search_type_code)
@@ -120,6 +121,7 @@ module Alexandria
120
121
  # list
121
122
  return
122
123
  end
124
+
123
125
  # ISBN-lookup results in multiple results (trying to be
124
126
  # useful, such as for new editions e.g. 9780974514055
125
127
  # "Programming Ruby" )
@@ -182,7 +184,7 @@ module Alexandria
182
184
  return [book, image_url]
183
185
 
184
186
  end
185
- rescue => ex
187
+ rescue StandardError => ex
186
188
  trace = ex.backtrace.join("\n> ")
187
189
  log.warn {
188
190
  'Failed parsing search results for Thalia ' \
@@ -1,24 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # -*- ruby -*-
3
+ # This file is part of Alexandria.
4
4
  #
5
- # Copyright (C) 2009 Cathal Mc Ginley
6
- # Copyright (C) 2014 Matijs van Zuijlen
7
- #
8
- # Alexandria is free software; you can redistribute it and/or
9
- # modify it under the terms of the GNU General Public License as
10
- # published by the Free Software Foundation; either version 2 of the
11
- # License, or (at your option) any later version.
12
- #
13
- # Alexandria is distributed in the hope that it will be useful,
14
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
15
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16
- # General Public License for more details.
17
- #
18
- # You should have received a copy of the GNU General Public
19
- # License along with Alexandria; see the file COPYING. If not,
20
- # write to the Free Software Foundation, Inc., 51 Franklin Street,
21
- # Fifth Floor, Boston, MA 02110-1301 USA.
5
+ # See the file README.md for authorship and licensing information.
22
6
 
23
7
  require 'hpricot'
24
8
  require 'htmlentities'
@@ -42,19 +26,17 @@ module Alexandria
42
26
  def text_of(node)
43
27
  if node.nil?
44
28
  nil
45
- else
46
- if node.text?
47
- node.to_html
48
- elsif node.elem?
49
- if node.children.nil?
50
- nil
51
- else
52
- node_text = node.children.map { |n| text_of(n) }.join
53
- node_text.strip.squeeze(' ')
54
- end
29
+ elsif node.text?
30
+ node.to_html
31
+ elsif node.elem?
32
+ if node.children.nil?
33
+ nil
34
+ else
35
+ node_text = node.children.map { |n| text_of(n) }.join
36
+ node_text.strip.squeeze(' ')
55
37
  end
56
- # node.inner_html.strip
57
38
  end
39
+ # node.inner_html.strip
58
40
  end
59
41
  end
60
42
  end
@@ -70,7 +70,7 @@ module Alexandria
70
70
 
71
71
  def url(book)
72
72
  create_search_uri(SEARCH_BY_ISBN, book.isbn)
73
- rescue => ex
73
+ rescue StandardError => ex
74
74
  log.warn { "Cannot create url for book #{book}; #{ex.message}" }
75
75
  nil
76
76
  end
@@ -78,9 +78,9 @@ module Alexandria
78
78
  private
79
79
 
80
80
  def create_search_uri(search_type, search_term)
81
- (search_type_code = { SEARCH_BY_ISBN => 'isbn:',
81
+ (search_type_code = { SEARCH_BY_ISBN => 'isbn:',
82
82
  SEARCH_BY_AUTHORS => 'au:',
83
- SEARCH_BY_TITLE => 'ti:',
83
+ SEARCH_BY_TITLE => 'ti:',
84
84
  SEARCH_BY_KEYWORD => '' }[search_type]) || ''
85
85
  search_type_code = CGI.escape(search_type_code)
86
86
  search_term_encoded = if search_type == SEARCH_BY_ISBN
@@ -106,6 +106,7 @@ module Alexandria
106
106
  result_cells.each do |td|
107
107
  type_icon = (td % 'div.type/img.icn')
108
108
  next unless type_icon && type_icon['src'] =~ /icon-bks/
109
+
109
110
  name_div = td % 'div.name'
110
111
  title = name_div.inner_text
111
112
  anchor = name_div % :a
@@ -117,7 +118,7 @@ module Alexandria
117
118
 
118
119
  book_search_results << result
119
120
  end
120
- rescue => ex
121
+ rescue StandardError => ex
121
122
  trace = ex.backtrace.join("\n> ")
122
123
  log.warn {
123
124
  'Failed parsing search results for WorldCat ' \
@@ -235,8 +236,9 @@ module Alexandria
235
236
  image_url = nil # hm, it's on the website, but uses JavaScript...
236
237
 
237
238
  return [book, image_url]
238
- rescue => ex
239
+ rescue StandardError => ex
239
240
  raise ex if ex.instance_of? NoResultsError
241
+
240
242
  trace = ex.backtrace.join("\n> ")
241
243
  log.warn {
242
244
  'Failed parsing search results for WorldCat ' \
@@ -62,6 +62,7 @@ module Alexandria
62
62
  resultset = search_records(criterion, type, conn_count)
63
63
  log.debug { "total #{resultset.length}" }
64
64
  raise NoResultsError if resultset.length == 0
65
+
65
66
  results = books_from_marc(resultset, isbn)
66
67
  type == SEARCH_BY_ISBN ? results.first : results
67
68
  end
@@ -75,12 +76,12 @@ module Alexandria
75
76
  def marc_to_book(marc_txt)
76
77
  begin
77
78
  marc = MARC::Record.new_from_marc(marc_txt, forgiving: true)
78
- rescue => ex
79
+ rescue StandardError => ex
79
80
  log.error { ex.message }
80
81
  log.error { ex.backtrace.join("> \n") }
81
82
  begin
82
83
  marc = MARC::Record.new(marc_txt)
83
- rescue => ex2
84
+ rescue StandardError => ex2
84
85
  log.error { ex2.message }
85
86
  log.error { ex2.backtrace.join("> \n") }
86
87
  raise ex2
@@ -132,7 +133,7 @@ module Alexandria
132
133
  # failing that, try the genuine MARC parser
133
134
  book = marc_to_book(marc_txt)
134
135
  end
135
- rescue => ex
136
+ rescue StandardError => ex
136
137
  log.warn { ex }
137
138
  log.warn { ex.backtrace }
138
139
  end
@@ -183,7 +184,7 @@ module Alexandria
183
184
  end
184
185
  end
185
186
  conn.search(pqf)
186
- rescue => ex
187
+ rescue StandardError => ex
187
188
  if /1005/ =~ ex.message
188
189
  if prefs.variable_named('piggyback') && prefs['piggyback']
189
190
  log.error { "Z39.50 search failed:: #{ex.message}" }
@@ -221,7 +222,7 @@ module Alexandria
221
222
  def url(book)
222
223
  'http://catalog.loc.gov/cgi-bin/Pwebrecon.cgi?DB=local&CNT=25+records+per+page&CMD=isbn+' +
223
224
  Library.canonicalise_isbn(book.isbn)
224
- rescue => ex
225
+ rescue StandardError => ex
225
226
  log.warn { "Cannot create url for book #{book}; #{ex.message}" }
226
227
  nil
227
228
  end
@@ -262,13 +263,14 @@ module Alexandria
262
263
  resultset = search_records(criterion, type, conn_count)
263
264
  log.debug { "total #{resultset.length}" }
264
265
  raise NoResultsError if resultset.length == 0
266
+
265
267
  results = books_from_sutrs(resultset)
266
268
  type == SEARCH_BY_ISBN ? results.first : results
267
269
  end
268
270
 
269
271
  def url(book)
270
272
  'http://copac.ac.uk/openurl?isbn=' + Library.canonicalise_isbn(book.isbn)
271
- rescue => ex
273
+ rescue StandardError => ex
272
274
  log.warn { "Cannot create url for book #{book}; #{ex.message}" }
273
275
  nil
274
276
  end
@@ -346,6 +348,7 @@ module Alexandria
346
348
  resultset = search_records(criterion, type, 0)
347
349
  log.debug { "total #{resultset.length}" }
348
350
  raise NoResultsError if resultset.length == 0
351
+
349
352
  results = books_from_marc(resultset, isbn)
350
353
  type == SEARCH_BY_ISBN ? results.first : results
351
354
  end
@@ -359,7 +362,7 @@ module Alexandria
359
362
  '%253C%2FFONT%253E%2522&session=&zurl=opac&zquery=%281%3D7+4%3D2+2%3D3+5%3D100+6%3D1+3%3D3+%22' +
360
363
  canonicalise_isbn_with_dashes(book.isbn) +
361
364
  '%22%29&language=it&maxentries=10&target=0&position=1'
362
- rescue => ex
365
+ rescue StandardError => ex
363
366
  log.warn { "Cannot create url for book #{book}; #{ex.message}" }
364
367
  nil
365
368
  end
@@ -1,27 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright (C) 2008 Joseph Method
3
+ # This file is part of Alexandria.
4
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.
5
+ # See the file README.md for authorship and licensing information.
19
6
 
20
7
  module Alexandria
21
8
  def self.list_books_on_console(_title = true, authors = true)
22
- libraries_simpleton = Alexandria::Libraries.instance
23
- libraries_simpleton.reload
24
- libraries = Alexandria::Library.loadall
9
+ collection = Alexandria::LibraryCollection.instance
10
+ collection.reload
11
+ libraries = collection.all_regular_libraries
25
12
  output_string = ''
26
13
  @books = libraries.flatten
27
14
  @books.each do |book|
@@ -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,6 +60,7 @@ 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
66
  if need_retval
@@ -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,77 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright (C) 2004-2006 Laurent Sansonetti
4
- # Copyright (C) 2007 Cathal Mc Ginley
5
- # Copyright (C) 2014, 2016 Matijs van Zuijlen
3
+ # This file is part of Alexandria.
6
4
  #
7
- # Alexandria is free software; you can redistribute it and/or
8
- # modify it under the terms of the GNU General Public License as
9
- # published by the Free Software Foundation; either version 2 of the
10
- # License, or (at your option) any later version.
11
- #
12
- # Alexandria is distributed in the hope that it will be useful,
13
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15
- # General Public License for more details.
16
- #
17
- # You should have received a copy of the GNU General Public
18
- # License along with Alexandria; see the file COPYING. If not,
19
- # write to the Free Software Foundation, Inc., 51 Franklin Street,
20
- # Fifth Floor, Boston, MA 02110-1301 USA.
21
-
22
- # Export sorting added 23 Oct 2007 by Cathal Mc Ginley
23
- # Classes LibrarySortOrder and SortedLibrary, and changed ExportFormat#invoke
24
- # iPod Notes support added 20 January 2008 by Tim Malone
25
- # require 'cgi'
5
+ # See the file README.md for authorship and licensing information.
26
6
 
27
7
  require 'csv'
28
8
  require 'image_size'
9
+ require 'tmpdir'
29
10
 
30
11
  module Alexandria
31
- class LibrarySortOrder
32
- include Logging
33
-
34
- def initialize(book_attribute, ascending = true)
35
- @book_attribute = book_attribute
36
- @ascending = ascending
37
- end
38
-
39
- def sort(library)
40
- sorted = library.sort_by do |book|
41
- book.send(@book_attribute)
42
- end
43
- sorted.reverse! unless @ascending
44
- sorted
45
- rescue => ex
46
- log.warn { "Could not sort library by #{@book_attribute.inspect}: #{ex.message}" }
47
- library
48
- end
49
-
50
- def to_s
51
- "#{@book_attribute} #{@ascending ? '(ascending)' : '(descending)'}"
52
- end
53
-
54
- class Unsorted < LibrarySortOrder
55
- def initialize; end
56
-
57
- def sort(library)
58
- library
59
- end
60
-
61
- def to_s
62
- 'default order'
63
- end
64
- end
65
- end
66
-
67
- class SortedLibrary < Library
12
+ class ExportLibrary
68
13
  def initialize(library, sort_order)
69
- super(library.name)
70
14
  @library = library
71
- sorted = sort_order.sort(library)
72
- sorted.each do |book|
73
- self << book
74
- end
15
+ @sorted = sort_order.sort(library)
75
16
  end
76
17
 
77
18
  def cover(book)
@@ -85,53 +26,15 @@ module Alexandria
85
26
  def copy_covers(dest)
86
27
  @library.copy_covers(dest)
87
28
  end
88
- end
89
-
90
- class ExportFormat
91
- attr_reader :name, :ext, :message
92
29
 
93
- include GetText
94
- include Logging
95
- extend GetText
96
- bindtextdomain(Alexandria::TEXTDOMAIN, charset: 'UTF-8')
97
-
98
- def self.all
99
- [
100
- new(_('Archived ONIX XML'), 'onix.tbz2', :export_as_onix_xml_archive),
101
- new(_('Archived Tellico XML'), 'tc', :export_as_tellico_xml_archive),
102
- new(_('BibTeX'), 'bib', :export_as_bibtex),
103
- new(_('CSV list'), 'csv', :export_as_csv_list),
104
- new(_('ISBN List'), 'txt', :export_as_isbn_list),
105
- new(_('iPod Notes'), nil, :export_as_ipod_notes),
106
- new(_('HTML Web Page'), nil, :export_as_html, true)
107
- ]
30
+ def name
31
+ @library.name
108
32
  end
109
33
 
110
- def invoke(library, sort_order, filename, *args)
111
- if sort_order
112
- sorted = SortedLibrary.new(library, sort_order)
113
- log.debug { "Exporting library sorted by #{sort_order}" }
114
- sorted.send(@message, filename, *args)
115
- else
116
- library.send(@message, filename, *args)
117
- end
34
+ def each(&block)
35
+ @sorted.each(&block)
118
36
  end
119
37
 
120
- def needs_preview?
121
- @needs_preview
122
- end
123
-
124
- private
125
-
126
- def initialize(name, ext, message, needs_preview = false)
127
- @name = name
128
- @ext = ext
129
- @message = message
130
- @needs_preview = needs_preview
131
- end
132
- end
133
-
134
- module Exportable
135
38
  def export_as_onix_xml_archive(filename)
136
39
  File.open(File.join(Dir.tmpdir, 'onix.xml'), 'w') do |io|
137
40
  to_onix_document.write(io, 0)
@@ -149,7 +52,7 @@ module Alexandria
149
52
  File.open(File.join(Dir.tmpdir, 'tellico.xml'), 'w') do |io|
150
53
  begin
151
54
  to_tellico_document.write(io, 0)
152
- rescue => ex
55
+ rescue StandardError => ex
153
56
  puts ex.message
154
57
  puts ex.backtrace
155
58
  raise ex
@@ -252,7 +155,7 @@ module Alexandria
252
155
  header.add_element('SentDate').text = format('%.4d%.2d%.2d%.2d%.2d',
253
156
  now.year, now.month, now.day, now.hour, now.min)
254
157
  header.add_element('MessageNote').text = name
255
- each_with_index do |book, idx|
158
+ @sorted.each_with_index do |book, idx|
256
159
  # fields that are missing: edition and rating.
257
160
  prod = msg.add_element('Product')
258
161
  prod.add_element('RecordReference').text = idx
@@ -325,7 +228,7 @@ module Alexandria
325
228
  # collection fields
326
229
  field1.add_attribute('name', '_default')
327
230
  images = collection.add_element('images')
328
- each_with_index do |book, idx|
231
+ @sorted.each_with_index do |book, idx|
329
232
  entry = collection.add_element('entry')
330
233
  new_index = (idx + 1).to_s
331
234
  entry.add_attribute('id', new_index)
@@ -350,9 +253,9 @@ module Alexandria
350
253
  image = images.add_element('image')
351
254
  image.add_attribute('id', final_cover(book))
352
255
  image_s = ImageSize.new(IO.read(cover(book)))
353
- image.add_attribute('height', image_s.get_height.to_s)
354
- image.add_attribute('width', image_s.get_width.to_s)
355
- image.add_attribute('format', image_s.get_type)
256
+ image.add_attribute('height', image_s.height.to_s)
257
+ image.add_attribute('width', image_s.width.to_s)
258
+ image.add_attribute('format', image_s.format)
356
259
  end
357
260
  end
358
261
  doc
@@ -370,95 +273,91 @@ module Alexandria
370
273
 
371
274
  def to_xhtml(css)
372
275
  generator = 'Alexandria ' + Alexandria::DISPLAY_VERSION
373
- xhtml = ''
374
- xhtml << <<EOS
375
- <?xml version="1.0" encoding="UTF-8"?>
376
- <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
377
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
378
- <html>
379
- <head>
380
- <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
381
- <meta name="Author" content="#{Etc.getlogin}"/>
382
- <meta name="Description" content="List of books"/>
383
- <meta name="Keywords" content="books"/>
384
- <meta name="Generator" content="#{xhtml_escape(generator)}"/>
385
- <title>#{xhtml_escape(name)}</title>
386
- <link rel="stylesheet" href="#{xhtml_escape(css)}" type="text/css"/>
387
- </head>
388
- <body>
389
- <h1 class="library_name">#{xhtml_escape(name)}</h1>
390
- EOS
276
+ xhtml = +''
277
+ xhtml << <<~EOS
278
+ <?xml version="1.0" encoding="UTF-8"?>
279
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
280
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
281
+ <html>
282
+ <head>
283
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
284
+ <meta name="Author" content="#{Etc.getlogin}"/>
285
+ <meta name="Description" content="List of books"/>
286
+ <meta name="Keywords" content="books"/>
287
+ <meta name="Generator" content="#{xhtml_escape(generator)}"/>
288
+ <title>#{xhtml_escape(name)}</title>
289
+ <link rel="stylesheet" href="#{xhtml_escape(css)}" type="text/css"/>
290
+ </head>
291
+ <body>
292
+ <h1 class="library_name">#{xhtml_escape(name)}</h1>
293
+ EOS
391
294
 
392
295
  each do |book|
393
- xhtml << <<EOS
394
- <div class="book">
395
- <p class="book_isbn">#{book.isbn}</p>
396
- EOS
296
+ xhtml << <<~EOS
297
+ <div class="book">
298
+ <p class="book_isbn">#{book.isbn}</p>
299
+ EOS
397
300
 
398
301
  if File.exist?(cover(book))
399
- xhtml << <<EOS
400
- <img class="book_cover"
401
- src="#{File.join('pixmaps', final_cover(book))}"
402
- alt="Cover file for '#{xhtml_escape(book.title)}'"
403
- EOS
404
302
  image_s = ImageSize.new(IO.read(cover(book)))
405
- xhtml << <<EOS
406
- height="#{image_s.get_height}" width="#{image_s.get_width}"
407
- EOS
408
- xhtml << <<EOS
409
- />
410
- EOS
303
+ xhtml << <<~EOS
304
+ <img class="book_cover"
305
+ src="#{File.join('pixmaps', final_cover(book))}"
306
+ alt="Cover file for '#{xhtml_escape(book.title)}'"
307
+ height="#{image_s.height}" width="#{image_s.width}"
308
+ />
309
+ EOS
411
310
  else
412
- xhtml << <<EOS
413
- <div class="no_book_cover"></div>
414
- EOS
311
+ xhtml << <<~EOS
312
+ <div class="no_book_cover"></div>
313
+ EOS
415
314
  end
416
315
 
417
316
  unless book.title.nil?
418
- xhtml << <<EOS
419
- <p class="book_title">#{xhtml_escape(book.title)}</p>
420
- EOS
317
+ xhtml << <<~EOS
318
+ <p class="book_title">#{xhtml_escape(book.title)}</p>
319
+ EOS
421
320
  end
422
321
 
423
322
  unless book.authors.empty?
424
323
  xhtml << '<ul class="book_authors">'
425
324
  book.authors.each do |author|
426
- xhtml << <<EOS
427
- <li class="book_author">#{xhtml_escape(author)}</li>
428
- EOS
325
+ xhtml << <<~EOS
326
+ <li class="book_author">#{xhtml_escape(author)}</li>
327
+ EOS
429
328
  end
430
329
  xhtml << '</ul>'
431
330
  end
432
331
 
433
332
  unless book.edition.nil?
434
- xhtml << <<EOS
435
- <p class="book_binding">#{xhtml_escape(book.edition)}</p>
436
- EOS
333
+ xhtml << <<~EOS
334
+ <p class="book_binding">#{xhtml_escape(book.edition)}</p>
335
+ EOS
437
336
  end
438
337
 
439
338
  unless book.publisher.nil?
440
- xhtml << <<EOS
441
- <p class="book_publisher">#{xhtml_escape(book.publisher)}</p>
442
- EOS
339
+ xhtml << <<~EOS
340
+ <p class="book_publisher">#{xhtml_escape(book.publisher)}</p>
341
+ EOS
443
342
  end
444
343
 
445
- xhtml << <<EOS
446
- </div>
447
- EOS
344
+ xhtml << <<~EOS
345
+ </div>
346
+ EOS
448
347
  end
449
- xhtml << <<EOS
450
- <p class="copyright">
451
- Generated on #{xhtml_escape(Date.today.to_s)}
452
- by <a href="#{xhtml_escape(Alexandria::WEBSITE_URL)}">#{xhtml_escape(generator)}</a>.
453
- </p>
454
- </body>
455
- </html>
456
- EOS
348
+ xhtml << <<~EOS
349
+ <p class="copyright">
350
+ Generated on #{xhtml_escape(Date.today.to_s)}
351
+ by <a href="#{xhtml_escape(Alexandria::WEBSITE_URL)}">#{xhtml_escape(generator)}</a>.
352
+ </p>
353
+ </body>
354
+ </html>
355
+ EOS
457
356
  end
458
357
 
459
358
  def to_bibtex
460
359
  generator = 'Alexandria ' + Alexandria::DISPLAY_VERSION
461
- bibtex = ''
360
+ bibtex = +''
462
361
  bibtex << "\%Generated on #{Date.today} by: #{generator}\n"
463
362
  bibtex << "\%\n"
464
363
  bibtex << "\n"
@@ -493,6 +392,7 @@ EOS
493
392
 
494
393
  def latex_escape(str)
495
394
  return '' if str.nil?
395
+
496
396
  my_str = str.dup
497
397
  my_str.gsub!(/%/, '\\%')
498
398
  my_str.gsub!(/~/, '\\textasciitilde')
@@ -506,12 +406,4 @@ EOS
506
406
  my_str
507
407
  end
508
408
  end
509
-
510
- class Library
511
- include Exportable
512
- end
513
-
514
- class SmartLibrary
515
- include Exportable
516
- end
517
409
  end