alexandria-book-collection-manager 0.7.8 → 0.7.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (134) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +48 -53
  3. data/.rubocop.yml +18 -5
  4. data/.rubocop_todo.yml +31 -37
  5. data/.simplecov +2 -2
  6. data/CHANGELOG.md +37 -0
  7. data/ChangeLog.0 +19 -19
  8. data/INSTALL.md +3 -5
  9. data/README.md +0 -5
  10. data/Rakefile +14 -14
  11. data/alexandria-book-collection-manager.gemspec +35 -34
  12. data/doc/FAQ +2 -2
  13. data/lib/alexandria/about.rb +1 -1
  14. data/lib/alexandria/book_providers/bl_provider.rb +88 -0
  15. data/lib/alexandria/book_providers/loc_provider.rb +38 -0
  16. data/lib/alexandria/book_providers/sbn_provider.rb +108 -0
  17. data/lib/alexandria/book_providers/thalia_provider.rb +1 -1
  18. data/lib/alexandria/book_providers/web.rb +2 -2
  19. data/lib/alexandria/book_providers/worldcat.rb +9 -7
  20. data/lib/alexandria/book_providers/z3950_provider.rb +199 -0
  21. data/lib/alexandria/book_providers.rb +10 -25
  22. data/lib/alexandria/console.rb +2 -2
  23. data/lib/alexandria/default_preferences.rb +1 -1
  24. data/lib/alexandria/export_library.rb +14 -14
  25. data/lib/alexandria/image_fetcher.rb +25 -0
  26. data/lib/alexandria/import_library.rb +10 -10
  27. data/lib/alexandria/library_store.rb +4 -5
  28. data/lib/alexandria/models/book.rb +13 -0
  29. data/lib/alexandria/models/library.rb +15 -23
  30. data/lib/alexandria/preferences.rb +4 -6
  31. data/lib/alexandria/{book_providers/pseudomarc.rb → pseudo_marc_parser.rb} +2 -2
  32. data/lib/alexandria/scanners/cue_cat.rb +1 -1
  33. data/lib/alexandria/smart_library.rb +2 -2
  34. data/lib/alexandria/ui/about_dialog.rb +1 -1
  35. data/lib/alexandria/ui/acquire_dialog.rb +6 -9
  36. data/lib/alexandria/ui/alert_dialog.rb +2 -2
  37. data/lib/alexandria/ui/barcode_animation.rb +1 -1
  38. data/lib/alexandria/ui/book_properties_dialog_base.rb +5 -9
  39. data/lib/alexandria/ui/completion_models.rb +1 -5
  40. data/lib/alexandria/ui/conflict_while_copying_dialog.rb +1 -1
  41. data/lib/alexandria/ui/icons.rb +2 -2
  42. data/lib/alexandria/ui/init.rb +10 -4
  43. data/lib/alexandria/ui/listview.rb +1 -1
  44. data/lib/alexandria/ui/multi_drag_treeview.rb +1 -1
  45. data/lib/alexandria/ui/new_book_dialog.rb +11 -13
  46. data/lib/alexandria/ui/new_book_dialog_manual.rb +1 -1
  47. data/lib/alexandria/ui/preferences_dialog.rb +2 -2
  48. data/lib/alexandria/ui/provider_preferences_base_dialog.rb +1 -1
  49. data/lib/alexandria/ui/really_delete_dialog.rb +1 -1
  50. data/lib/alexandria/ui/ui_manager.rb +17 -25
  51. data/lib/alexandria/version.rb +1 -1
  52. data/lib/alexandria/web_themes.rb +1 -1
  53. data/lib/alexandria.rb +6 -5
  54. data/po/cs.po +90 -125
  55. data/po/cy.po +87 -125
  56. data/po/de.po +96 -125
  57. data/po/el.po +96 -125
  58. data/po/es.po +96 -125
  59. data/po/fr.po +90 -125
  60. data/po/ga.po +83 -124
  61. data/po/gl.po +90 -125
  62. data/po/it.po +90 -125
  63. data/po/ja.po +90 -125
  64. data/po/mk.po +96 -125
  65. data/po/nb.po +90 -125
  66. data/po/nl.po +107 -124
  67. data/po/pl.po +113 -124
  68. data/po/pt.po +90 -125
  69. data/po/pt_BR.po +90 -125
  70. data/po/ru.po +92 -124
  71. data/po/sk.po +90 -125
  72. data/po/sv.po +90 -125
  73. data/po/uk.po +90 -125
  74. data/po/zh_TW.po +90 -125
  75. data/schemas/alexandria.schemas +1 -1
  76. data/share/gnome/help/alexandria/C/adding-books.xml +3 -4
  77. data/share/gnome/help/alexandria/C/introduction.xml +0 -16
  78. data/share/gnome/help/alexandria/C/searching.xml +1 -4
  79. data/share/gnome/help/alexandria/C/settings.xml +0 -30
  80. data/share/gnome/help/alexandria/fr/alexandria.xml +4 -159
  81. data/share/gnome/help/alexandria/ja/adding-books.xml +1 -1
  82. data/share/gnome/help/alexandria/ja/introduction.xml +0 -15
  83. data/share/gnome/help/alexandria/ja/searching.xml +3 -7
  84. data/share/gnome/help/alexandria/ja/settings.xml +0 -27
  85. data/spec/alexandria/book_providers/bl_provider_spec.rb +13 -0
  86. data/spec/alexandria/book_providers/loc_provider_spec.rb +17 -0
  87. data/spec/alexandria/book_providers/sbn_provider_spec.rb +13 -0
  88. data/spec/alexandria/book_providers/thalia_provider_spec.rb +1 -1
  89. data/spec/alexandria/book_providers/world_cat_provider_spec.rb +22 -10
  90. data/spec/alexandria/book_providers_spec.rb +0 -81
  91. data/spec/alexandria/book_spec.rb +5 -3
  92. data/spec/alexandria/export_library_spec.rb +8 -8
  93. data/spec/alexandria/library_spec.rb +83 -51
  94. data/spec/alexandria/library_store_spec.rb +1 -1
  95. data/spec/alexandria/preferences_spec.rb +7 -7
  96. data/spec/alexandria/pseudo_marc_parser_spec.rb +71 -0
  97. data/spec/alexandria/scanners/cue_cat_spec.rb +11 -4
  98. data/spec/alexandria/smart_library_spec.rb +7 -5
  99. data/spec/alexandria/ui/about_dialog_spec.rb +1 -1
  100. data/spec/alexandria/ui/acquire_dialog_spec.rb +1 -1
  101. data/spec/alexandria/ui/alert_dialog_spec.rb +5 -3
  102. data/spec/alexandria/ui/bad_isbns_dialog_spec.rb +1 -1
  103. data/spec/alexandria/ui/book_properties_dialog_spec.rb +4 -4
  104. data/spec/alexandria/ui/confirm_erase_dialog_spec.rb +18 -2
  105. data/spec/alexandria/ui/conflict_while_copying_dialog_spec.rb +1 -1
  106. data/spec/alexandria/ui/error_dialog_spec.rb +13 -2
  107. data/spec/alexandria/ui/export_dialog_spec.rb +3 -3
  108. data/spec/alexandria/ui/iconview_spec.rb +1 -1
  109. data/spec/alexandria/ui/import_dialog_spec.rb +3 -3
  110. data/spec/alexandria/ui/keep_bad_isbn_dialog_spec.rb +1 -1
  111. data/spec/alexandria/ui/new_book_dialog_manual_spec.rb +4 -4
  112. data/spec/alexandria/ui/new_book_dialog_spec.rb +2 -2
  113. data/spec/alexandria/ui/new_provider_dialog_spec.rb +3 -3
  114. data/spec/alexandria/ui/new_smart_library_dialog_spec.rb +9 -7
  115. data/spec/alexandria/ui/preferences_dialog_spec.rb +1 -1
  116. data/spec/alexandria/ui/provider_preferences_dialog_spec.rb +2 -2
  117. data/spec/alexandria/ui/really_delete_dialog_spec.rb +1 -1
  118. data/spec/alexandria/ui/sidepane_manager_spec.rb +1 -1
  119. data/spec/alexandria/ui/skip_entry_dialog_spec.rb +18 -2
  120. data/spec/alexandria/ui/smart_library_properties_dialog_spec.rb +2 -2
  121. data/spec/alexandria/ui/ui_manager_spec.rb +83 -5
  122. data/spec/data/libraries/0.6.2/My Library/9780571147168.yaml +2 -0
  123. data/spec/spec_helper.rb +23 -32
  124. data/util/rake/fileinstall.rb +12 -12
  125. data/util/rake/gettextgenerate.rb +1 -1
  126. data/util/rake/omfgenerate.rb +1 -1
  127. metadata +73 -58
  128. data/lib/alexandria/book_providers/adlibris.rb +0 -191
  129. data/lib/alexandria/book_providers/amazon_aws.rb +0 -239
  130. data/lib/alexandria/book_providers/amazon_ecs_util.rb +0 -373
  131. data/lib/alexandria/book_providers/barnes_and_noble.rb +0 -209
  132. data/lib/alexandria/book_providers/proxis.rb +0 -176
  133. data/lib/alexandria/book_providers/siciliano.rb +0 -256
  134. data/lib/alexandria/book_providers/z3950.rb +0 -408
@@ -35,12 +35,16 @@ module Alexandria
35
35
  SEARCH_BY_KEYWORD = (0..3).to_a
36
36
 
37
37
  class SearchError < StandardError; end
38
+
38
39
  class NoResultsError < SearchError; end
40
+
39
41
  class TooManyResultsError < SearchError; end
42
+
40
43
  class InvalidSearchTypeError < SearchError; end
41
44
 
42
45
  # These errors are not really errors
43
46
  class ProviderSkippedError < NoResultsError; end
47
+
44
48
  class SearchEmptyError < SearchError; end
45
49
 
46
50
  def self.search(criterion, type)
@@ -93,14 +97,9 @@ module Alexandria
93
97
 
94
98
  when SocketError
95
99
  format(_("Couldn't reach the provider '%s': socket " \
96
- "error (%s)."), factory.name, ex.message)
100
+ "error (%s)."), factory.name, ex.message)
97
101
 
98
- when NoResultsError
99
- _("No results were found. Make sure your " \
100
- "search criterion is spelled correctly, and " \
101
- "try again.")
102
-
103
- when ProviderSkippedError
102
+ when NoResultsError, ProviderSkippedError
104
103
  _("No results were found. Make sure your " \
105
104
  "search criterion is spelled correctly, and " \
106
105
  "try again.")
@@ -115,7 +114,7 @@ module Alexandria
115
114
  ex.message
116
115
  end
117
116
  log.debug { "raising empty error #{message}" }
118
- raise SearchEmptyError, message # rubocop:disable GetText/DecorateFunctionMessage
117
+ raise SearchEmptyError, message # rubocop:disable I18n/GetText/DecorateFunctionMessage
119
118
  else
120
119
  factory_n += 1
121
120
  retry
@@ -291,19 +290,14 @@ module Alexandria
291
290
 
292
291
  require "alexandria/book_providers/douban" # only requires YAML
293
292
 
294
- # Amazon AWS (Amazon Associates Web Services) provider, needs hpricot
295
- require "alexandria/book_providers/amazon_aws"
296
-
297
293
  # Website based providers
298
- require "alexandria/book_providers/adlibris"
299
- require "alexandria/book_providers/barnes_and_noble"
300
- require "alexandria/book_providers/proxis"
301
- require "alexandria/book_providers/siciliano"
302
294
  require "alexandria/book_providers/thalia_provider"
303
295
  require "alexandria/book_providers/worldcat"
304
296
 
305
297
  # Z39.50 based providers
306
- require "alexandria/book_providers/z3950"
298
+ require "alexandria/book_providers/loc_provider"
299
+ require "alexandria/book_providers/bl_provider"
300
+ require "alexandria/book_providers/sbn_provider"
307
301
 
308
302
  attr_reader :abstract_classes
309
303
 
@@ -381,19 +375,10 @@ module Alexandria
381
375
 
382
376
  changed = false
383
377
 
384
- if (ecs_index = priority.index("AmazonECS"))
385
- priority[ecs_index] = "Amazon" # replace legacy "AmazonECS" name
386
- priority.uniq! # remove any other "Amazon" from the list
387
- changed = true
388
- end
389
378
  if (worldcat_index = priority.index("Worldcat"))
390
379
  priority[worldcat_index] = "WorldCat"
391
380
  changed = true
392
381
  end
393
- if (adlibris_index = priority.index("Adlibris"))
394
- priority[adlibris_index] = "AdLibris"
395
- changed = true
396
- end
397
382
  @prefs.providers_priority = priority if changed
398
383
  end
399
384
  end
@@ -5,14 +5,14 @@
5
5
  # See the file README.md for authorship and licensing information.
6
6
 
7
7
  module Alexandria
8
- def self.list_books_on_console(_title = true, authors = true)
8
+ def self.list_books_on_console
9
9
  collection = Alexandria::LibraryCollection.instance
10
10
  collection.reload
11
11
  libraries = collection.all_regular_libraries
12
12
  output_string = ""
13
13
  @books = libraries.flatten
14
14
  @books.each do |book|
15
- book_authors = book.authors.join(" & ") if authors
15
+ book_authors = book.authors.join(" & ")
16
16
  output_string += [book.title, book_authors].join(", ") + "\n"
17
17
  end
18
18
  output_string
@@ -30,7 +30,7 @@ module Alexandria
30
30
  "col_want_visible" => true,
31
31
  "col_tags_visible" => true,
32
32
  "cols_width" => "{}",
33
- "providers_priority" => ["Amazon", "BarnesAndNoble", "AdLibris", "Proxis", "Thalia", "Siciliano", "WorldCat", "LOC", "BL", "SBN"],
33
+ "providers_priority" => ["Thalia", "WorldCat", "LOC", "BL", "SBN"],
34
34
  "view_advanced_settings" => false
35
35
  }
36
36
  end
@@ -73,7 +73,7 @@ module Alexandria
73
73
  end
74
74
 
75
75
  def export_as_html(filename, theme)
76
- FileUtils.mkdir(filename) unless File.exist?(filename)
76
+ FileUtils.mkdir_p(filename)
77
77
  Dir.chdir(filename) do
78
78
  copy_covers("pixmaps")
79
79
  FileUtils.cp_r(theme.pixmaps_directory, "pixmaps") if theme.has_pixmaps?
@@ -91,7 +91,7 @@ module Alexandria
91
91
  end
92
92
 
93
93
  def export_as_ipod_notes(filename, _theme)
94
- FileUtils.mkdir(filename) unless File.exist?(filename)
94
+ FileUtils.mkdir_p(filename)
95
95
  tempdir = Dir.getwd
96
96
  Dir.chdir(filename)
97
97
  copy_covers("pixmaps")
@@ -211,8 +211,8 @@ module Alexandria
211
211
  doc = REXML::Document.new
212
212
  doc << REXML::XMLDecl.new
213
213
  doc << REXML::DocType.new("tellico",
214
- 'PUBLIC "-//Robby Stephenson/DTD Tellico V7.0//EN"' \
215
- ' "http://periapsis.org/tellico/dtd/v7/tellico.dtd"')
214
+ 'PUBLIC "-//Robby Stephenson/DTD Tellico V7.0//EN" ' \
215
+ '"http://periapsis.org/tellico/dtd/v7/tellico.dtd"')
216
216
  tellico = doc.add_element("tellico")
217
217
  tellico.add_attribute("syntaxVersion", "7")
218
218
  tellico.add_namespace("http://periapsis.org/tellico/")
@@ -251,7 +251,7 @@ module Alexandria
251
251
  entry.add_element("cover").text = final_cover(book)
252
252
  image = images.add_element("image")
253
253
  image.add_attribute("id", final_cover(book))
254
- image_s = ImageSize.new(IO.read(cover(book)))
254
+ image_s = ImageSize.new(File.read(cover(book)))
255
255
  image.add_attribute("height", image_s.height.to_s)
256
256
  image.add_attribute("width", image_s.width.to_s)
257
257
  image.add_attribute("format", image_s.format)
@@ -298,7 +298,7 @@ module Alexandria
298
298
  EOS
299
299
 
300
300
  if File.exist?(cover(book))
301
- image_s = ImageSize.new(IO.read(cover(book)))
301
+ image_s = ImageSize.new(File.read(cover(book)))
302
302
  xhtml << <<~EOS
303
303
  <img class="book_cover"
304
304
  src="#{File.join('pixmaps', final_cover(book))}"
@@ -374,7 +374,7 @@ module Alexandria
374
374
  bibtex << 'author = "'
375
375
  if book.authors != []
376
376
  bibtex << book.authors[0]
377
- book.authors[1..-1].each do |author|
377
+ book.authors[1..].each do |author|
378
378
  bibtex << " and #{latex_escape(author)}"
379
379
  end
380
380
  end
@@ -395,13 +395,13 @@ module Alexandria
395
395
  return "" if str.nil?
396
396
 
397
397
  my_str = str.dup
398
- my_str.gsub!(/%/, '\\%')
399
- my_str.gsub!(/~/, '\\textasciitilde')
400
- my_str.gsub!(/&/, '\\\\&')
401
- my_str.gsub!(/\#/, '\\\\#')
402
- my_str.gsub!(/\{/, '\\{')
403
- my_str.gsub!(/\}/, '\\}')
404
- my_str.gsub!(/_/, '\\_')
398
+ my_str.gsub!(/%/, "\\%")
399
+ my_str.gsub!(/~/, "\\textasciitilde")
400
+ my_str.gsub!(/&/, "\\\\&")
401
+ my_str.gsub!(/\#/, "\\\\#")
402
+ my_str.gsub!(/\{/, "\\{")
403
+ my_str.gsub!(/\}/, "\\}")
404
+ my_str.gsub!(/_/, "\\_")
405
405
  my_str.gsub!(/\$/, "\\\$")
406
406
  my_str.gsub!(/"(.+)"/, "``\1''")
407
407
  my_str
@@ -0,0 +1,25 @@
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
+ require "alexandria/net"
8
+
9
+ module Alexandria
10
+ module ImageFetcher
11
+ def fetch_image(uri)
12
+ result = nil
13
+ if URI.parse(uri).scheme.nil?
14
+ File.open(uri, "r") do |io|
15
+ result = io.read
16
+ end
17
+ else
18
+ result = WWWAgent.new.get(uri)
19
+ end
20
+ result
21
+ rescue Errno::ECONNRESET
22
+ nil
23
+ end
24
+ end
25
+ end
@@ -53,13 +53,13 @@ module Alexandria
53
53
  def self.import_autodetect(*args)
54
54
  log.debug { args.inspect }
55
55
  filename = args[1]
56
- log.debug { "Filename is #{filename} and ext is #{filename[-4..-1]}" }
56
+ log.debug { "Filename is #{filename} and ext is #{filename[-4..]}" }
57
57
  log.debug { "Beginning import: #{args[0]}, #{args[1]}" }
58
- if filename[-4..-1] == ".txt"
58
+ if filename[-4..] == ".txt"
59
59
  import_as_isbn_list(*args)
60
- elsif [".tc", ".bc"].include? filename[-3..-1]
60
+ elsif [".tc", ".bc"].include? filename[-3..]
61
61
  import_as_tellico_xml_archive(*args)
62
- elsif [".csv"].include? filename[-4..-1]
62
+ elsif [".csv"].include? filename[-4..]
63
63
  import_as_csv_file(*args)
64
64
  else
65
65
  raise _("Unsupported type")
@@ -72,7 +72,7 @@ module Alexandria
72
72
  return nil unless system("unzip -qqt \"#{filename}\"")
73
73
 
74
74
  tmpdir = File.join(Dir.tmpdir, "tellico_export")
75
- FileUtils.rm_rf(tmpdir) if File.exist?(tmpdir)
75
+ FileUtils.rm_rf(tmpdir)
76
76
  Dir.mkdir(tmpdir)
77
77
  Dir.chdir(tmpdir) do
78
78
  system("unzip -qq \"#{filename}\"")
@@ -97,11 +97,11 @@ module Alexandria
97
97
  keys = ["isbn", "publisher", "pub_year", "binding"]
98
98
 
99
99
  book_elements = [neaten(elements["title"].text)]
100
- book_elements += if !elements["authors"].nil?
100
+ book_elements += if elements["authors"].nil?
101
+ [[]]
102
+ else
101
103
  [elements["authors"].elements.to_a.map \
102
104
  { |x| neaten(x.text) }]
103
- else
104
- [[]]
105
105
  end
106
106
  book_elements += keys.map do |key|
107
107
  neaten(elements[key].text) if elements[key]
@@ -139,7 +139,7 @@ module Alexandria
139
139
  def self.import_as_csv_file(name, filename, on_iterate_cb, _on_error_cb)
140
140
  require "alexandria/import_library_csv"
141
141
  books_and_covers = []
142
- line_count = IO.readlines(filename).reduce(0) { |count, _line| count + 1 }
142
+ line_count = File.readlines(filename).reduce(0) { |count, _line| count + 1 }
143
143
 
144
144
  import_count = 0
145
145
  max_import = line_count - 1
@@ -210,7 +210,7 @@ module Alexandria
210
210
  def self.import_as_isbn_list(name, filename, on_iterate_cb,
211
211
  on_error_cb)
212
212
  log.debug { "Starting import_as_isbn_list... " }
213
- isbn_list = IO.readlines(filename).map do |line|
213
+ isbn_list = File.readlines(filename).map do |line|
214
214
  log.debug { "Trying line #{line}" }
215
215
  [line.chomp, canonicalise_isbn(line.chomp)] unless line == "\n"
216
216
  end
@@ -43,7 +43,7 @@ module Alexandria
43
43
  test = [0, nil]
44
44
  ruined_books = []
45
45
  library = Library.new(name, self)
46
- FileUtils.mkdir_p(library.path) unless File.exist?(library.path)
46
+ FileUtils.mkdir_p(library.path)
47
47
  Dir.chdir(library.path) do
48
48
  Dir["*" + Library::EXT[:book]].sort.each do |filename|
49
49
  test[1] = filename if (test[0]).zero?
@@ -152,7 +152,7 @@ module Alexandria
152
152
  # Skip non-regular files.
153
153
  next unless File.stat(filename).file?
154
154
 
155
- text = IO.read(filename)
155
+ text = File.read(filename)
156
156
  hash = YAML.safe_load(text, permitted_classes: [Symbol])
157
157
  smart_library = SmartLibrary.from_hash(hash, self)
158
158
  a << smart_library
@@ -181,7 +181,7 @@ module Alexandria
181
181
  private
182
182
 
183
183
  def regularize_book_from_yaml(name)
184
- text = IO.read(name)
184
+ text = File.read(name)
185
185
 
186
186
  # Code to remove the mystery string in books imported from Amazon
187
187
  # (In the past, still?) To allow ruby-amazon to be removed.
@@ -202,8 +202,7 @@ module Alexandria
202
202
  text.sub!(md[0], "loaned_since: #{new_yaml}\n")
203
203
  end
204
204
 
205
- # TODO: Ensure book loading passes through Book#initialize
206
- book = YAML.safe_load(text, permitted_classes: [Book, Time])
205
+ book = Book.from_yaml(text)
207
206
 
208
207
  unless book.isbn.instance_of? String
209
208
  # HACK
@@ -71,5 +71,18 @@ module Alexandria
71
71
  def inspect
72
72
  "#<Alexandria::Book title: #{@title}>"
73
73
  end
74
+
75
+ def self.from_yaml(text)
76
+ node = YAML.parse_stream text
77
+ doc = node.children.first
78
+ mapping = doc.children.first
79
+ mapping.children.each_slice(2) do |_k, v|
80
+ v.tag = nil if v.tag == "!ruby/array:Alexandria:Library"
81
+ end
82
+ yaml = node.to_yaml
83
+
84
+ # TODO: Ensure book loading passes through Book#initialize
85
+ YAML.safe_load(yaml, permitted_classes: [Book, Time])
86
+ end
74
87
  end
75
88
  end
@@ -10,15 +10,17 @@ require "rexml/document"
10
10
  require "tempfile"
11
11
  require "etc"
12
12
  require "alexandria/library_store"
13
+ require "alexandria/image_fetcher"
13
14
 
14
15
  module Alexandria
15
16
  class Library < Array
16
17
  include Logging
18
+ include ImageFetcher
17
19
 
18
20
  attr_reader :name
19
21
  attr_accessor :ruined_books, :updating, :deleted_books
20
22
 
21
- DEFAULT_DIR = File.join(ENV["HOME"], ".alexandria")
23
+ DEFAULT_DIR = File.join(Dir.home, ".alexandria")
22
24
  EXT = { book: ".yaml", cover: ".cover" }.freeze
23
25
 
24
26
  include GetText
@@ -77,7 +79,7 @@ module Alexandria
77
79
  normalized = entry.delete("- ").upcase
78
80
  return [] unless /\A[\dX]*\Z/.match?(normalized)
79
81
 
80
- normalized.split("").map do |char|
82
+ normalized.chars.map do |char|
81
83
  char == "X" ? 10 : char.to_i
82
84
  end
83
85
  end
@@ -238,19 +240,13 @@ module Alexandria
238
240
  Dir.chdir(path) do
239
241
  # Fetch the cover picture.
240
242
  cover_file = cover(book)
241
- File.open(cover_file, "w") do |io|
242
- uri = URI.parse(cover_uri)
243
- if uri.scheme.nil?
244
- # Regular filename.
245
- File.open(cover_uri) { |io2| io.puts io2.read }
246
- else
247
- # Try open-uri.
248
- io.puts transport.get(uri)
249
- end
250
- end
243
+ data = fetch_image(cover_uri)
244
+ if data
245
+ File.write(cover_file, data)
251
246
 
252
- # Remove the file if its blank.
253
- File.delete(cover_file) if Alexandria::UI::Icons.blank?(cover_file)
247
+ # Remove the file if its blank.
248
+ File.delete(cover_file) if Alexandria::UI::Icons.blank?(cover_file)
249
+ end
254
250
  end
255
251
  end
256
252
 
@@ -345,9 +341,7 @@ module Alexandria
345
341
  else
346
342
  "g#{something.ident}" # g is for generated id...
347
343
  end
348
- when String
349
- something
350
- when Integer
344
+ when String, Integer
351
345
  something
352
346
  else
353
347
  raise NotImplementedError
@@ -359,9 +353,7 @@ module Alexandria
359
353
  ident = case something
360
354
  when Book
361
355
  something.ident
362
- when String
363
- something
364
- when Integer
356
+ when String, Integer
365
357
  something
366
358
  else
367
359
  raise NotImplementedError
@@ -370,7 +362,7 @@ module Alexandria
370
362
  end
371
363
 
372
364
  def name=(name)
373
- File.rename(path, File.join(dir, name))
365
+ File.rename(path, File.join(@store.library_dir, name))
374
366
  @name = name
375
367
  end
376
368
 
@@ -387,7 +379,7 @@ module Alexandria
387
379
  end
388
380
 
389
381
  def copy_covers(somewhere)
390
- FileUtils.rm_rf(somewhere) if File.exist?(somewhere)
382
+ FileUtils.rm_rf(somewhere)
391
383
  FileUtils.mkdir(somewhere)
392
384
  each do |book|
393
385
  next unless File.exist?(cover(book))
@@ -398,7 +390,7 @@ module Alexandria
398
390
  end
399
391
 
400
392
  def self.jpeg?(file)
401
- IO.read(file, 10)[6..9] == "JFIF"
393
+ File.read(file, 10)[6..9] == "JFIF"
402
394
  end
403
395
 
404
396
  def final_cover(book)
@@ -135,8 +135,6 @@ module Alexandria
135
135
 
136
136
  def get_gconf_type(value)
137
137
  case value
138
- when String
139
- "string"
140
138
  when Integer
141
139
  "int"
142
140
  when TrueClass, FalseClass
@@ -147,7 +145,7 @@ module Alexandria
147
145
  end
148
146
 
149
147
  def exec_gconf_set_list(var_path, new_list)
150
- # NOTE we must check between list and pair...
148
+ # NOTE: we must check between list and pair...
151
149
 
152
150
  list_type = get_gconf_type(new_list.first)
153
151
  if list_type == "int" && new_list.size == 2
@@ -238,11 +236,11 @@ module Alexandria
238
236
  # range of values used by Alexandria.
239
237
  def discriminate(value)
240
238
  case value
241
- when "true" # bool
239
+ when "true" # bool
242
240
  true
243
- when "false" # bool
241
+ when "false" # bool
244
242
  false
245
- when /^[0-9]+$/ # int
243
+ when /^-?[0-9]+$/ # int
246
244
  value.to_i
247
245
  when /^\[(.*)\]$/ # list (assume of type String)
248
246
  Regexp.last_match[1].split(",")
@@ -124,7 +124,7 @@ module Alexandria
124
124
 
125
125
  def self.marc_text_to_details(marc)
126
126
  details = {}
127
- marc.each_line do |line|
127
+ marc&.each_line do |line|
128
128
  if line =~ /(\d+)\s*(.+)/
129
129
  code = Regexp.last_match[1]
130
130
  data = Regexp.last_match[2]
@@ -133,7 +133,7 @@ module Alexandria
133
133
 
134
134
  d_idx = 0
135
135
  while d_idx < data.size
136
- d_str = data[d_idx..-1]
136
+ d_str = data[d_idx..]
137
137
  idx = d_str =~ /\$([a-z]) ([^$]+)/
138
138
  break unless idx
139
139
 
@@ -81,7 +81,7 @@ module Alexandria
81
81
  def decode_field(encoded)
82
82
  seq = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+-"
83
83
 
84
- chars = encoded.split(//)
84
+ chars = encoded.chars
85
85
  values = chars.map { |c| seq.index(c) }
86
86
 
87
87
  padding = pad(values)
@@ -148,7 +148,7 @@ module Alexandria
148
148
  if book
149
149
  @cache[book].save(book)
150
150
  else
151
- FileUtils.mkdir_p(base_dir) unless File.exist? base_dir
151
+ FileUtils.mkdir_p(base_dir)
152
152
  File.open(yaml, "w") { |io| io.puts to_hash.to_yaml }
153
153
  end
154
154
  end
@@ -162,7 +162,7 @@ module Alexandria
162
162
  end
163
163
 
164
164
  def copy_covers(somewhere)
165
- FileUtils.rm_rf(somewhere) if File.exist?(somewhere)
165
+ FileUtils.rm_rf(somewhere)
166
166
  FileUtils.mkdir(somewhere)
167
167
  each do |book|
168
168
  library = @cache[book]
@@ -7,7 +7,7 @@
7
7
  module Alexandria
8
8
  module UI
9
9
  class AboutDialog
10
- GPL = <<~EOL # rubocop:disable GetText/DecorateString
10
+ GPL = <<~EOL # rubocop:disable I18n/GetText/DecorateString
11
11
  Alexandria is free software; you can redistribute it and/or
12
12
  modify it under the terms of the GNU General Public License as
13
13
  published by the Free Software Foundation; either version 2 of the
@@ -5,6 +5,9 @@
5
5
  # See the file README.md for authorship and licensing information.
6
6
 
7
7
  require "monitor"
8
+
9
+ require "alexandria/image_fetcher"
10
+
8
11
  require "alexandria/scanners/cue_cat"
9
12
  require "alexandria/scanners/keyboard"
10
13
 
@@ -40,6 +43,7 @@ module Alexandria
40
43
 
41
44
  class AcquireDialog < BuilderBase
42
45
  include GetText
46
+ include ImageFetcher
43
47
  include Logging
44
48
  extend GetText
45
49
  GetText.bindtextdomain(Alexandria::TEXTDOMAIN, charset: "UTF-8")
@@ -91,7 +95,7 @@ module Alexandria
91
95
  library, is_new_library =
92
96
  @combo_libraries.selection_from_libraries(libraries)
93
97
 
94
- # NOTE at this stage, the ISBN is 10-digit...
98
+ # NOTE: at this stage, the ISBN is 10-digit...
95
99
  #
96
100
 
97
101
  selection = @barcodes_treeview.selection
@@ -357,14 +361,7 @@ module Alexandria
357
361
  Thread.new do
358
362
  pixbuf = nil
359
363
  if cover_uri
360
- image_data = nil
361
- if URI.parse(cover_uri).scheme.nil?
362
- File.open(cover_uri, "r") do |io|
363
- image_data = io.read
364
- end
365
- else
366
- image_data = URI.parse(cover_uri).read
367
- end
364
+ image_data = fetch_image(cover_uri)
368
365
  loader = GdkPixbuf::PixbufLoader.new
369
366
  loader.last_write(image_data)
370
367
  pixbuf = loader.pixbuf
@@ -34,6 +34,8 @@ module Alexandria
34
34
  @dialog.child.pack_start(hbox)
35
35
  end
36
36
 
37
+ attr_reader :dialog
38
+
37
39
  def show_all
38
40
  dialog.show_all
39
41
  end
@@ -52,8 +54,6 @@ module Alexandria
52
54
 
53
55
  private
54
56
 
55
- attr_reader :dialog
56
-
57
57
  def make_label(markup)
58
58
  label = Gtk::Label.new
59
59
  label.set_alignment(0, 0)
@@ -121,7 +121,7 @@ module Alexandria
121
121
  until d.empty?
122
122
  space_width = d[0].chr.to_i
123
123
  bar_width = d[1].chr.to_i
124
- d = d[2..-1]
124
+ d = d[2..]
125
125
  @barcode_data << [space_width, bar_width]
126
126
  end
127
127
  end
@@ -146,16 +146,12 @@ module Alexandria
146
146
  end
147
147
 
148
148
  def own_toggled
149
- @checkbutton_want.inconsistent = if @checkbutton_own.active?
150
- true
151
- else
152
- false
153
- end
149
+ @checkbutton_want.inconsistent = @checkbutton_own.active?
154
150
  end
155
151
 
156
152
  def want_toggled; end
157
153
 
158
- @@latest_filechooser_directory = ENV["HOME"]
154
+ @@latest_filechooser_directory = Dir.home
159
155
  def on_change_cover
160
156
  dialog = Gtk::FileChooserDialog.new(title: _("Select a cover image"),
161
157
  parent: @book_properties_dialog,
@@ -181,8 +177,8 @@ module Alexandria
181
177
  FileUtils.cp(dialog.filename, "#{@cover_file}.orig")
182
178
  new_width = cover.width / (cover.height / COVER_ABSOLUTE_MAXHEIGHT.to_f)
183
179
  log.info do
184
- "Scaling large cover image to" \
185
- " #{new_width.to_i} x #{COVER_ABSOLUTE_MAXHEIGHT}"
180
+ "Scaling large cover image to " \
181
+ "#{new_width.to_i} x #{COVER_ABSOLUTE_MAXHEIGHT}"
186
182
  end
187
183
  cover = cover.scale(new_width.to_i, COVER_ABSOLUTE_MAXHEIGHT)
188
184
  cover.save(@cover_file, "jpeg")
@@ -268,7 +264,7 @@ module Alexandria
268
264
  raise _("out of range") if rating < 0 || rating > images.length
269
265
 
270
266
  images[0..rating - 1].each { |x| x.pixbuf = Icons::STAR_SET }
271
- images[rating..-1].each { |x| x.pixbuf = Icons::STAR_UNSET }
267
+ images[rating..].each { |x| x.pixbuf = Icons::STAR_UNSET }
272
268
  @current_rating = rating
273
269
  end
274
270
 
@@ -44,11 +44,7 @@ module Alexandria
44
44
  cur_tag = key.split(",").last.strip
45
45
  if cur_tag.size >= min
46
46
  begin
47
- if /^#{cur_tag}/.match?(iter[0])
48
- true
49
- else
50
- false
51
- end
47
+ /^#{cur_tag}/.match?(iter[0])
52
48
  rescue StandardError
53
49
  false
54
50
  end
@@ -15,7 +15,7 @@ module Alexandria
15
15
  def initialize(parent, library, book)
16
16
  super(parent,
17
17
  format(_("The book '%s' already exists in '%s'. Would you like " \
18
- "to replace it?"), book.title, library.name),
18
+ "to replace it?"), book.title, library.name),
19
19
  Gtk::Stock::DIALOG_QUESTION,
20
20
  [[_("_Skip"), Gtk::ResponseType::CANCEL],
21
21
  [_("_Replace"), Gtk::ResponseType::OK]],
@@ -35,8 +35,8 @@ module Alexandria
35
35
  return GdkPixbuf::Pixbuf.new(file: filename) if File.exist?(filename)
36
36
  rescue GdkPixbuf::PixbufError
37
37
  log.error do
38
- "Failed to load GdkPixbuf::Pixbuf," \
39
- " please ensure that #{filename} is a valid image file"
38
+ "Failed to load GdkPixbuf::Pixbuf, " \
39
+ "please ensure that #{filename} is a valid image file"
40
40
  end
41
41
  end
42
42
  BOOK_ICON