alexandria-book-collection-manager 0.6.9 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.rubocop.yml +34 -30
  4. data/.rubocop_todo.yml +139 -54
  5. data/CHANGELOG.md +10 -0
  6. data/Gemfile +1 -0
  7. data/Rakefile +10 -11
  8. data/alexandria-book-collection-manager.gemspec +3 -2
  9. data/bin/alexandria +1 -1
  10. data/lib/alexandria.rb +3 -6
  11. data/lib/alexandria/about.rb +9 -9
  12. data/lib/alexandria/book_providers.rb +12 -12
  13. data/lib/alexandria/book_providers/adlibris.rb +14 -18
  14. data/lib/alexandria/book_providers/amazon_aws.rb +17 -31
  15. data/lib/alexandria/book_providers/amazon_ecs_util.rb +5 -6
  16. data/lib/alexandria/book_providers/barnes_and_noble.rb +51 -76
  17. data/lib/alexandria/book_providers/bol_it.rb +12 -12
  18. data/lib/alexandria/book_providers/deastore.rb +27 -31
  19. data/lib/alexandria/book_providers/douban.rb +9 -13
  20. data/lib/alexandria/book_providers/ibs_it.rb +10 -10
  21. data/lib/alexandria/book_providers/mcu.rb +12 -18
  22. data/lib/alexandria/book_providers/proxis.rb +14 -22
  23. data/lib/alexandria/book_providers/pseudomarc.rb +8 -18
  24. data/lib/alexandria/book_providers/renaud.rb +16 -16
  25. data/lib/alexandria/book_providers/siciliano.rb +25 -38
  26. data/lib/alexandria/book_providers/thalia.rb +13 -16
  27. data/lib/alexandria/book_providers/webster_it.rb +14 -18
  28. data/lib/alexandria/book_providers/worldcat.rb +21 -25
  29. data/lib/alexandria/book_providers/z3950.rb +19 -23
  30. data/lib/alexandria/config.rb +2 -2
  31. data/lib/alexandria/execution_queue.rb +3 -1
  32. data/lib/alexandria/export_library.rb +19 -22
  33. data/lib/alexandria/import_library.rb +14 -18
  34. data/lib/alexandria/import_library_csv.rb +12 -30
  35. data/lib/alexandria/models/book.rb +7 -9
  36. data/lib/alexandria/models/library.rb +44 -44
  37. data/lib/alexandria/net.rb +1 -1
  38. data/lib/alexandria/preferences.rb +12 -57
  39. data/lib/alexandria/scanners.rb +10 -6
  40. data/lib/alexandria/scanners/cuecat.rb +2 -2
  41. data/lib/alexandria/smart_library.rb +12 -12
  42. data/lib/alexandria/ui.rb +5 -2
  43. data/lib/alexandria/ui/callbacks.rb +106 -65
  44. data/lib/alexandria/ui/completion_models.rb +55 -51
  45. data/lib/alexandria/ui/dialogs/about_dialog.rb +1 -1
  46. data/lib/alexandria/ui/dialogs/acquire_dialog.rb +25 -51
  47. data/lib/alexandria/ui/dialogs/alert_dialog.rb +13 -11
  48. data/lib/alexandria/ui/dialogs/bad_isbns_dialog.rb +2 -2
  49. data/lib/alexandria/ui/dialogs/barcode_animation.rb +39 -23
  50. data/lib/alexandria/ui/dialogs/book_properties_dialog.rb +16 -21
  51. data/lib/alexandria/ui/dialogs/book_properties_dialog_base.rb +23 -24
  52. data/lib/alexandria/ui/dialogs/export_dialog.rb +46 -45
  53. data/lib/alexandria/ui/dialogs/import_dialog.rb +26 -35
  54. data/lib/alexandria/ui/dialogs/misc_dialogs.rb +11 -11
  55. data/lib/alexandria/ui/dialogs/new_book_dialog.rb +47 -59
  56. data/lib/alexandria/ui/dialogs/new_book_dialog_manual.rb +14 -13
  57. data/lib/alexandria/ui/dialogs/new_smart_library_dialog.rb +12 -11
  58. data/lib/alexandria/ui/dialogs/preferences_dialog.rb +37 -43
  59. data/lib/alexandria/ui/dialogs/smart_library_properties_dialog.rb +9 -8
  60. data/lib/alexandria/ui/dialogs/smart_library_properties_dialog_base.rb +47 -53
  61. data/lib/alexandria/ui/dndable.rb +5 -4
  62. data/lib/alexandria/ui/icons.rb +19 -19
  63. data/lib/alexandria/ui/iconview.rb +7 -12
  64. data/lib/alexandria/ui/iconview_tooltips.rb +22 -109
  65. data/lib/alexandria/ui/init.rb +7 -15
  66. data/lib/alexandria/ui/libraries_combo.rb +54 -48
  67. data/lib/alexandria/ui/listview.rb +30 -85
  68. data/lib/alexandria/ui/multi_drag_treeview.rb +110 -107
  69. data/lib/alexandria/ui/sidepane.rb +23 -25
  70. data/lib/alexandria/ui/sound.rb +18 -27
  71. data/lib/alexandria/ui/ui_manager.rb +126 -204
  72. data/lib/alexandria/undo_manager.rb +2 -2
  73. data/lib/alexandria/version.rb +4 -4
  74. data/spec/alexandria/book_providers_spec.rb +7 -4
  75. data/spec/alexandria/library_spec.rb +13 -16
  76. data/spec/alexandria/scanners/cuecat_spec.rb +1 -2
  77. data/spec/alexandria/ui/dialogs_spec.rb +5 -1
  78. data/spec/alexandria/ui/main_app_spec.rb +3 -3
  79. data/{lib/alexandria/utils.rb → spec/alexandria/ui/sound_spec.rb} +6 -11
  80. data/spec/alexandria/ui/ui_utilities_spec.rb +3 -3
  81. data/spec/spec_helper.rb +2 -2
  82. data/util/rake/fileinstall.rb +17 -33
  83. data/util/rake/gettextgenerate.rb +2 -4
  84. data/util/rake/omfgenerate.rb +1 -3
  85. metadata +23 -11
  86. data/lib/alexandria/ui/gtk_thread_help.rb +0 -89
@@ -46,11 +46,12 @@ Gem::Specification.new do |s|
46
46
  s.add_runtime_dependency('gettext', ['~> 3.1'])
47
47
  s.add_runtime_dependency('hpricot', ['~> 0.8.5'])
48
48
  s.add_runtime_dependency('htmlentities', ['~> 4.3'])
49
- s.add_runtime_dependency('gtk2', ['~> 3.0'])
49
+ s.add_runtime_dependency('gtk3', ['~> 3.0.9'])
50
+ s.add_runtime_dependency('gio2', ['~> 3.0.9'])
50
51
  s.add_runtime_dependency('gstreamer', ['~> 3.0'])
51
52
 
52
53
  s.add_development_dependency('minitest', ['~> 5.0'])
53
- s.add_development_dependency('rake', ['~> 10.0'])
54
+ s.add_development_dependency('rake', ['~> 11.1'])
54
55
  s.add_development_dependency('rspec', ['~> 3.0'])
55
56
 
56
57
  s.require_paths = ['lib']
@@ -3,7 +3,7 @@
3
3
  # Copyright (C) 2004-2006 Laurent Sansonetti
4
4
  # Copyright (C) 2007, 2008 Joseph Method
5
5
  # Copyright (C) 2007, 2009 Cathal Mc Ginley
6
- # Copyright (C) 2014 Matijs van Zuijlen
6
+ # Copyright (C) 2014, 2015 Matijs van Zuijlen
7
7
  #
8
8
  # Alexandria is free software; you can redistribute it and/or
9
9
  # modify it under the terms of the GNU General Public License as
@@ -1,5 +1,5 @@
1
1
  # Copyright (C) 2004-2006 Laurent Sansonetti
2
- # Copyright (C) 2011 Matijs van Zuijlen
2
+ # Copyright (C) 2011, 2016 Matijs van Zuijlen
3
3
  #
4
4
  # Alexandria is free software; you can redistribute it and/or
5
5
  # modify it under the terms of the GNU General Public License as
@@ -34,13 +34,11 @@ require 'alexandria/about'
34
34
 
35
35
  module Alexandria
36
36
  def self.set_proxy
37
- ENV['http_proxy'] = nil if !ENV['http_proxy'].nil? and URI.parse(ENV['http_proxy']).userinfo.nil?
37
+ ENV['http_proxy'] = nil if !ENV['http_proxy'].nil? && URI.parse(ENV['http_proxy']).userinfo.nil?
38
38
  end
39
39
 
40
40
  def self.set_log_level
41
- if $DEBUG
42
- Alexandria.log.level = Logger::DEBUG
43
- end
41
+ Alexandria.log.level = Logger::DEBUG if $DEBUG
44
42
  Alexandria.log.debug { 'Initializing Alexandria...' }
45
43
  end
46
44
 
@@ -64,7 +62,6 @@ require 'alexandria/version'
64
62
  # VERSION = OSX::NSBundle.mainBundle.infoDictionary.objectForKey('CFBundleVersion').to_s
65
63
  # end
66
64
  # end
67
- require 'alexandria/utils'
68
65
 
69
66
  require 'alexandria/models/book'
70
67
  require 'alexandria/models/library'
@@ -19,13 +19,13 @@
19
19
  # Fifth Floor, Boston, MA 02110-1301 USA.
20
20
 
21
21
  module Alexandria
22
- TITLE = 'Alexandria'
23
- TEXTDOMAIN = 'alexandria'
22
+ TITLE = 'Alexandria'.freeze
23
+ TEXTDOMAIN = 'alexandria'.freeze
24
24
  extend GetText
25
25
  bindtextdomain(Alexandria::TEXTDOMAIN, charset: 'UTF-8')
26
26
  DESCRIPTION = _('A program to help you manage your book collection.')
27
27
  COPYRIGHT = "Copyright (C) 2004-2006 Laurent Sansonetti\n" \
28
- 'Copyright (C) 2007-2010,2014,2015 Alexandria Contributors'
28
+ 'Copyright (C) 2007-2010,2014,2015 Alexandria Contributors'.freeze
29
29
  AUTHORS = [
30
30
  'Alexander McCormmach <alexander@tunicate.org>',
31
31
  'Aymeric Nys <aymeric@nnx.com>',
@@ -48,11 +48,11 @@ module Alexandria
48
48
  'Takayuki Kusano <AE5T-KSN@asahi-net.or.jp>',
49
49
  'Timothy Malone <timothy.malone@gmail.com>',
50
50
  'Zachary P. Landau <kapheine@hypa.net>'
51
- ]
51
+ ].freeze
52
52
  DOCUMENTERS = [
53
53
  'Cathal Mc Ginley <cathal.alexandria@gnostai.org>',
54
54
  'Liam Davison <registrations@liamjdavison.info>'
55
- ]
55
+ ].freeze
56
56
  TRANSLATORS = [
57
57
  'Adrián Chaves Fernández <adriyetichaves@gmail.com> (gl)',
58
58
  'Cathal Mc Ginley <cathal.alexandria@gnostai.org> (ga)',
@@ -72,11 +72,11 @@ module Alexandria
72
72
  'Petr Vanek <vanous@penguin.cz> (cs)',
73
73
  'Piotr Drąg <piotrdrag@gmail.com> (pl)',
74
74
  'Serhij Dubyk <dubyk@library.lviv.ua> (uk)'
75
- ]
75
+ ].freeze
76
76
  ARTISTS = [
77
77
  'Andreas Nilsson <nisses.mail@home.se>',
78
78
  'Stefanie Dijoux <stefanie.dijoux@gmail.com>'
79
- ]
80
- BUGREPORT_URL = 'http://www.github.com/mvz/alexandria-book-collection-manager/issues'
81
- WEBSITE_URL = 'http://www.github.com/mvz/alexandria-book-collection-manager'
79
+ ].freeze
80
+ BUGREPORT_URL = 'http://www.github.com/mvz/alexandria-book-collection-manager/issues'.freeze
81
+ WEBSITE_URL = 'http://www.github.com/mvz/alexandria-book-collection-manager'.freeze
82
82
  end
@@ -57,7 +57,7 @@ module Alexandria
57
57
  # sanity check if at least one valid result is actually found
58
58
  results.delete_if { |book, _cover| book.nil? }
59
59
 
60
- if results.length == 0
60
+ if results.empty?
61
61
  instance.changed
62
62
  instance.notify_observers(:not_found, factory.fullname) # new
63
63
  raise NoResultsError
@@ -187,7 +187,7 @@ module Alexandria
187
187
  each do |var|
188
188
  message = @provider.variable_name(var)
189
189
  val = Alexandria::Preferences.instance.send(message)
190
- var.value = val unless val.nil? or (val == '' and var.mandatory?)
190
+ var.value = val unless val.nil? || ((val == '') && var.mandatory?)
191
191
  end
192
192
  end
193
193
  end
@@ -199,7 +199,7 @@ module Alexandria
199
199
 
200
200
  def initialize(name, fullname = nil)
201
201
  @name = name
202
- @fullname = (fullname or name)
202
+ @fullname = (fullname || name)
203
203
  @prefs = Preferences.new(self)
204
204
  @prefs.add('enabled', _('Enabled'), true, [true, false])
205
205
  end
@@ -231,7 +231,7 @@ module Alexandria
231
231
  ary.delete(@name)
232
232
  prefs.abstract_providers = ary
233
233
  end
234
- if (ary = prefs.providers_priority) and ary.include?(@name)
234
+ if (ary = prefs.providers_priority) && ary.include?(@name)
235
235
  ary.delete(@name)
236
236
  prefs.providers_priority = ary
237
237
  end
@@ -266,7 +266,7 @@ module Alexandria
266
266
  end
267
267
 
268
268
  def self.abstract?
269
- (!included_modules.include?(Singleton))
269
+ !included_modules.include?(Singleton)
270
270
  end
271
271
 
272
272
  def <=>(provider)
@@ -333,10 +333,10 @@ module Alexandria
333
333
  md = /(.+)Provider$/.match(constant)
334
334
  next unless md
335
335
  klass = self.class.module_eval(constant.to_s)
336
- if klass.ancestors.include?(AbstractProvider) and
337
- klass != GenericProvider and
338
- klass != WebsiteBasedProvider and
339
- klass != AbstractProvider
336
+ if klass.ancestors.include?(AbstractProvider) &&
337
+ (klass != GenericProvider) &&
338
+ (klass != WebsiteBasedProvider) &&
339
+ (klass != AbstractProvider)
340
340
 
341
341
  if klass.abstract?
342
342
  @abstract_classes << klass
@@ -363,12 +363,12 @@ module Alexandria
363
363
  end
364
364
  clear
365
365
  rejig_providers_priority
366
- priority = (@prefs.providers_priority or [])
366
+ priority = (@prefs.providers_priority || [])
367
367
  priority.map!(&:strip)
368
368
  rest = providers.keys - priority
369
369
  priority.each { |pname| self << providers[pname] }
370
370
  rest.sort.each { |pname| self << providers[pname] }
371
- self.compact!
371
+ compact!
372
372
  end
373
373
 
374
374
  # FIXME: Define the handful of methods that use this.
@@ -383,7 +383,7 @@ module Alexandria
383
383
  private
384
384
 
385
385
  def rejig_providers_priority
386
- priority = (@prefs.providers_priority or [])
386
+ priority = (@prefs.providers_priority || [])
387
387
  unless priority.empty?
388
388
  changed = false
389
389
 
@@ -33,12 +33,12 @@ module Alexandria
33
33
  class AdLibrisProvider < WebsiteBasedProvider
34
34
  include Alexandria::Logging
35
35
 
36
- SITE = 'http://www.adlibris.com/se/'
36
+ SITE = 'http://www.adlibris.com/se/'.freeze
37
37
 
38
38
  BASE_SEARCH_URL = "#{SITE}searchresult.aspx?search=advanced&%s=%s" \
39
- '&fromproduct=False' # type/term
39
+ '&fromproduct=False'.freeze # type/term
40
40
 
41
- PRODUCT_URL = "#{SITE}product.aspx?isbn=%s"
41
+ PRODUCT_URL = "#{SITE}product.aspx?isbn=%s".freeze
42
42
 
43
43
  def initialize
44
44
  super('AdLibris', 'AdLibris (Sweden)')
@@ -76,17 +76,17 @@ module Alexandria
76
76
  if search_type == SEARCH_BY_ISBN
77
77
  PRODUCT_URL % Library.canonicalise_isbn(search_term)
78
78
  else
79
- search_type_code = {
79
+ (search_type_code = {
80
80
  SEARCH_BY_AUTHORS => 'author',
81
81
  SEARCH_BY_TITLE => 'title',
82
82
  SEARCH_BY_KEYWORD => 'keyword'
83
- }[search_type] or 'keyword'
83
+ }[search_type]) || 'keyword'
84
84
  search_term_encoded = CGI.escape(search_term)
85
85
  BASE_SEARCH_URL % [search_type_code, search_term_encoded]
86
86
  end
87
87
  end
88
88
 
89
- # TODO use Iconv to pre-convert the html.body to UTF-8 everywhere
89
+ # TODO: use Iconv to pre-convert the html.body to UTF-8 everywhere
90
90
  # before sending it to the parser methods
91
91
 
92
92
  def get_book_from_search_result(rslt)
@@ -187,28 +187,24 @@ module Alexandria
187
187
 
188
188
  isbn_tds.each do |isbn_td|
189
189
  isbn = isbn_td.inner_text
190
- unless isbn =~ /[0-9x]{10,13}/i
191
- next
192
- end
190
+ next unless isbn =~ /[0-9x]{10,13}/i
193
191
  isbn.gsub(/(\n|\r)/, ' ')
194
192
  if isbn =~ /:[\s]*([0-9x]+)/i
195
193
  isbn = Regexp.last_match[1]
196
194
  end
197
195
  isbns << isbn
198
196
  end
199
- isbn = isbns.first
200
- if isbn
201
- isbn = Library.canonicalise_isbn(isbn)
202
- end
197
+ isbn = isbns.first
198
+ isbn = Library.canonicalise_isbn(isbn) if isbn
203
199
 
204
200
  # cover
205
201
  image_url = nil
206
202
  if (cover_img = doc.search('span.imageWithShadow img[@id$="ProductImageNotLinked"]').first)
207
- if cover_img['src'] =~ /^http\:\/\//
208
- image_url = cover_img['src']
209
- else
210
- image_url = "#{SITE}/#{cover_img['src']}" # HACK use html base
211
- end
203
+ image_url = if cover_img['src'] =~ /^http\:\/\//
204
+ cover_img['src']
205
+ else
206
+ "#{SITE}/#{cover_img['src']}" # HACK: use html base
207
+ end
212
208
  if image_url =~ /noimage.gif$/
213
209
  # no point downloading a "no image" graphic
214
210
  # Alexandria has its own generic book icon...
@@ -1,6 +1,6 @@
1
1
  # Copyright (C) 2004-2006 Laurent Sansonetti
2
2
  # Copyright (C) 2008 Cathal Mc Ginley
3
- # Copyright (C) 2014 Matijs van Zuijlen
3
+ # Copyright (C) 2014, 2016 Matijs van Zuijlen
4
4
  #
5
5
  # Alexandria is free software; you can redistribute it and/or
6
6
  # modify it under the terms of the GNU General Public License as
@@ -31,7 +31,7 @@ module Alexandria
31
31
 
32
32
  # CACHE_DIR = File.join(Alexandria::Library::DIR, '.amazon_cache')
33
33
 
34
- LOCALES = ['ca', 'de', 'fr', 'jp', 'uk', 'us']
34
+ LOCALES = ['ca', 'de', 'fr', 'jp', 'uk', 'us'].freeze
35
35
 
36
36
  def initialize
37
37
  super('Amazon', 'Amazon')
@@ -46,17 +46,17 @@ module Alexandria
46
46
  # kill old (shorter) tokens, or previously distributed Access Key Id (see #26250)
47
47
 
48
48
  if token
49
- if (token.value != token.value.strip)
49
+ if token.value != token.value.strip
50
50
  token.new_value = token.value.strip
51
51
  end
52
52
  end
53
- if token and (token.value.size != 20 or token.value == '0J356Z09CN88KB743582')
53
+ if token && ((token.value.size != 20) || (token.value == '0J356Z09CN88KB743582'))
54
54
  token.new_value = ''
55
55
  end
56
56
 
57
57
  secret = prefs.variable_named('secret_key')
58
58
  if secret
59
- if (secret.value != secret.value.strip)
59
+ if secret.value != secret.value.strip
60
60
  secret.new_value = secret.value.strip
61
61
  end
62
62
  end
@@ -66,7 +66,7 @@ module Alexandria
66
66
  if associate.value.strip.empty?
67
67
  associate.new_value = 'rubyalexa-20'
68
68
  end
69
- if (associate.value != associate.value.strip)
69
+ if associate.value != associate.value.strip
70
70
  associate.new_value = associate.value.strip
71
71
  end
72
72
  end
@@ -76,13 +76,13 @@ module Alexandria
76
76
  prefs.read
77
77
 
78
78
  if prefs['secret_key'].empty?
79
- raise Amazon::RequestError.new('Secret Access Key required for Authentication: you must sign up for your own Amazon AWS account')
79
+ raise Amazon::RequestError, 'Secret Access Key required for Authentication: you must sign up for your own Amazon AWS account'
80
80
  end
81
81
 
82
82
  if (config = Alexandria::Preferences.instance.http_proxy_config)
83
83
  host, port, user, pass = config
84
84
  url = 'http://'
85
- url += user + ':' + pass + '@' if user and pass
85
+ url += user + ':' + pass + '@' if user && pass
86
86
  url += host + ':' + port.to_s
87
87
  ENV['http_proxy'] = url
88
88
  end
@@ -105,7 +105,7 @@ module Alexandria
105
105
  when SEARCH_BY_ISBN
106
106
  criterion = Library.canonicalise_isbn(criterion)
107
107
  # This isn't ideal : I'd like to do an ISBN/EAN-specific search
108
- res = Amazon::Ecs.item_search(criterion, response_group: 'ItemAttributes,Images', country: request_locale)
108
+ res = Amazon::Ecs.item_search(criterion, response_group: 'ItemAttributes,Images', country: request_locale)
109
109
  res.items.each do |item|
110
110
  products << item
111
111
  end
@@ -129,10 +129,10 @@ module Alexandria
129
129
  end
130
130
 
131
131
  when SEARCH_BY_TITLE
132
- res = Amazon::Ecs.item_search(criterion, response_group: 'ItemAttributes,Images', country: request_locale)
132
+ res = Amazon::Ecs.item_search(criterion, response_group: 'ItemAttributes,Images', country: request_locale)
133
133
 
134
134
  res.items.each do |item|
135
- if /#{criterion}/i.match(item.get('itemattributes/title'))
135
+ if item.get('itemattributes/title') =~ /#{criterion}/i
136
136
  products << item
137
137
  end
138
138
  end
@@ -140,14 +140,14 @@ module Alexandria
140
140
 
141
141
  when SEARCH_BY_AUTHORS
142
142
  criterion = "author:#{criterion}"
143
- res = Amazon::Ecs.item_search(criterion, response_group: 'ItemAttributes,Images', country: request_locale, type: 'Power')
143
+ res = Amazon::Ecs.item_search(criterion, response_group: 'ItemAttributes,Images', country: request_locale, type: 'Power')
144
144
  res.items.each do |item|
145
145
  products << item
146
146
  end
147
147
  # #req.author_search(criterion) do |product|
148
148
 
149
149
  when SEARCH_BY_KEYWORD
150
- res = Amazon::Ecs.item_search(criterion, response_group: 'ItemAttributes,Images', country: request_locale)
150
+ res = Amazon::Ecs.item_search(criterion, response_group: 'ItemAttributes,Images', country: request_locale)
151
151
 
152
152
  res.items.each do |item|
153
153
  products << item
@@ -172,25 +172,13 @@ module Alexandria
172
172
  atts = item.search_and_convert('itemattributes')
173
173
  title = normalize(atts.get('title'))
174
174
 
175
- # Work around Amazon US encoding bug. Amazon US apparently
176
- # interprets UTF-8 titles as ISO-8859 titles and then converts
177
- # the garbled titles to UTF-8. This tries to convert back into
178
- # valid UTF-8. It does not always work - see isbn 2259196098
179
- # (from the mailing list) for an example.
180
- # if req.locale == 'us'
181
- # title = title.convert('ISO-8859-1','UTF-8')
182
- # end
183
- # Cathal Mc Ginley 2008-02-18, still a problem for that ISBN!! Yep 2009-12-09!
184
-
185
175
  media = normalize(atts.get('binding'))
186
176
  media = nil if media == 'Unknown Binding'
187
177
 
188
178
  isbn = normalize(atts.get('isbn'))
189
- if isbn and Library.valid_isbn?(isbn)
190
- isbn = Library.canonicalise_ean(isbn)
191
- else
192
- isbn = nil # it may be an ASIN which is not an ISBN
193
- end
179
+ isbn = if isbn && Library.valid_isbn?(isbn)
180
+ Library.canonicalise_ean(isbn)
181
+ end
194
182
  # hack, extract year by regexp (not Y10K compatible :-)
195
183
  /([1-9][0-9]{3})/ =~ atts.get('publicationdate')
196
184
  publishing_year = Regexp.last_match[1] ? Regexp.last_match[1].to_i : nil
@@ -251,9 +239,7 @@ module Alexandria
251
239
  end
252
240
 
253
241
  def normalize(str)
254
- unless str.nil?
255
- str = str.squeeze(' ').strip
256
- end
242
+ str = str.squeeze(' ').strip unless str.nil?
257
243
  str
258
244
  end
259
245
  end
@@ -45,8 +45,7 @@ module Amazon
45
45
  ca: 'http://webservices.amazon.ca/onca/xml?Service=AWSECommerceService',
46
46
  de: 'http://webservices.amazon.de/onca/xml?Service=AWSECommerceService',
47
47
  jp: 'http://webservices.amazon.co.jp/onca/xml?Service=AWSECommerceService',
48
- fr: 'http://webservices.amazon.fr/onca/xml?Service=AWSECommerceService'
49
- }
48
+ fr: 'http://webservices.amazon.fr/onca/xml?Service=AWSECommerceService' }.freeze
50
49
 
51
50
  @@options = {}
52
51
  @@debug = false
@@ -202,12 +201,12 @@ module Amazon
202
201
 
203
202
  def self.prepare_url(opts)
204
203
  country = opts.delete(:country)
205
- country = (country.nil?) ? 'us' : country
204
+ country = country.nil? ? 'us' : country
206
205
  request_url = SERVICE_URLS[country.to_sym]
207
206
  raise Amazon::RequestError, "Invalid country '#{country}'" unless request_url
208
207
 
209
208
  qs = ''
210
- opts.each {|k, v|
209
+ opts.each { |k, v|
211
210
  next unless v
212
211
  v = v.join(',') if v.is_a? Array
213
212
  qs << "&#{camelize(k.to_s)}=#{URI.encode(v.to_s)}"
@@ -317,7 +316,7 @@ module Amazon
317
316
  # Find Hpricot::Elements matching the given path. Example: element/"author".
318
317
  def /(path)
319
318
  elements = @element / path
320
- return nil if elements.size == 0
319
+ return nil if elements.empty?
321
320
  elements
322
321
  end
323
322
 
@@ -374,7 +373,7 @@ module Amazon
374
373
  result = element / path
375
374
  if (result.is_a? Hpricot::Elements) || (result.is_a? Array)
376
375
  parsed_result = []
377
- result.each {|item|
376
+ result.each { |item|
378
377
  parsed_result << Element.get(item)
379
378
  }
380
379
  parsed_result
@@ -1,7 +1,7 @@
1
1
  # -*- ruby -*-
2
2
  #
3
3
  # Copyright (C) 2009 Cathal Mc Ginley
4
- # Copyright (C) 2011, 2014 Matijs van Zuijlen
4
+ # Copyright (C) 2011, 2014, 2015 Matijs van Zuijlen
5
5
  #
6
6
  # Alexandria is free software; you can redistribute it and/or
7
7
  # modify it under the terms of the GNU General Public License as
@@ -35,13 +35,12 @@ module Alexandria
35
35
  class BarnesAndNobleProvider < WebsiteBasedProvider
36
36
  include Alexandria::Logging
37
37
 
38
- SITE = 'http://www.barnesandnoble.com'
38
+ SITE = 'http://www.barnesandnoble.com'.freeze
39
39
 
40
- BASE_ISBN_SEARCH_URL = 'http://search.barnesandnoble.com/books' \
41
- '/product.aspx?ISBSRC=Y&ISBN=%s'
40
+ BASE_ISBN_SEARCH_URL = 'http://www.barnesandnoble.com/s/%s'.freeze
42
41
 
43
42
  BASE_SEARCH_URL = 'http://search.barnesandnoble.com/booksearch' \
44
- '/results.asp?%s=%s' # type, term
43
+ '/results.asp?%s=%s'.freeze # type, term
45
44
 
46
45
  def initialize
47
46
  super('BarnesAndNoble', 'BarnesAndNoble')
@@ -50,25 +49,28 @@ module Alexandria
50
49
  end
51
50
 
52
51
  def agent
53
- unless @agent
54
- @agent = Alexandria::WWWAgent.new
55
- end
52
+ @agent = Alexandria::WWWAgent.new unless @agent
56
53
  @agent
57
54
  end
58
55
 
59
56
  def fetch_redirectly(uri_str, limit = 5)
60
- raise NoResultsError, 'HTTP redirect too deep' if limit == 0
61
- response = agent.get(uri_str)
57
+ raise NoResultsError, 'HTTP redirect too deep' if limit.zero?
62
58
  if limit < 10
63
59
  sleep 0.1
64
- puts "Redirectly :: #{uri_str}"
60
+ log.debug { "Redirectly :: #{uri_str}" }
65
61
  else
66
- puts "Fetching :: #{uri_str}"
62
+ log.debug { "Fetching :: #{uri_str}" }
67
63
  end
68
- puts response.inspect
64
+ response = agent.get(uri_str)
65
+ log.debug { response.inspect }
69
66
  case response
70
67
  when Net::HTTPSuccess then response
71
- when Net::HTTPRedirection then fetch_redirectly(response['Location'], (limit - 1))
68
+ when Net::HTTPRedirection then
69
+ redirect = URI.parse response['Location']
70
+ if redirect.relative?
71
+ redirect = URI.parse(uri_str) + redirect
72
+ end
73
+ fetch_redirectly(redirect.to_s, (limit - 1))
72
74
  else
73
75
  response.error!
74
76
  end
@@ -97,11 +99,11 @@ module Alexandria
97
99
  end
98
100
 
99
101
  def create_search_uri(search_type, search_term)
100
- search_type_code = {
102
+ (search_type_code = {
101
103
  SEARCH_BY_AUTHORS => 'ATH',
102
104
  SEARCH_BY_TITLE => 'TTL',
103
- SEARCH_BY_KEYWORD => 'WRD' # SEARCH_BY_PUBLISHER => 'PBL' # not implemented
104
- }[search_type] or ''
105
+ SEARCH_BY_KEYWORD => 'WRD' # SEARCH_BY_PUBLISHER => 'PBL' # not implemented
106
+ }[search_type]) || ''
105
107
  if search_type == SEARCH_BY_ISBN
106
108
  BASE_ISBN_SEARCH_URL % Library.canonicalise_ean(search_term) # isbn-13
107
109
  else
@@ -112,7 +114,7 @@ module Alexandria
112
114
 
113
115
  def get_book_from_search_result(result)
114
116
  log.debug { "Fetching book from #{result[:url]}" }
115
- html_data = transport.get_response(URI.parse(result[:url]))
117
+ html_data = transport.get_response(URI.parse(result[:url]))
116
118
  parse_result_data(html_data.body)
117
119
  end
118
120
 
@@ -146,71 +148,44 @@ module Alexandria
146
148
  doc = html_to_doc(html)
147
149
  begin
148
150
  book_data = {}
149
- title_header = doc % '//div.wgt-productTitle/h1'
150
- if title_header
151
- title = ''
152
- title_header.children.each do |node|
153
- if node.text?
154
- title += ' ' + node.to_s
155
- end
156
- end
157
- title.strip!
158
- if title.empty?
159
- log.warn { 'Unexpectedly found no title in BarnesAndNoble lookup' }
160
- raise NoResultsError
161
- end
162
- book_data[:title] = title.strip.squeeze(' ')
163
- subtitle_span = title_header % 'span.subtitle'
164
- if subtitle_span
165
- book_data[:title] += " #{subtitle_span.inner_text}"
166
- end
167
- end
168
-
169
- isbn_links = doc / '//a.isbn-a'
170
- isbns = isbn_links.map(&:inner_text)
171
- book_data[:isbn] = Library.canonicalise_ean(isbns.first)
172
151
 
173
- authors = []
174
- author_links = title_header / 'a[@href*="ATH"]'
175
- author_links.each do |a|
176
- authors << a.inner_text
152
+ dl = (doc / 'dl').first
153
+ dts = dl.children_of_type('dt')
154
+ dts.each do |dt|
155
+ value = dt.next_sibling.inner_text
156
+ case dt.inner_text
157
+ when /ISBN-13/
158
+ book_data[:isbn] = Library.canonicalise_ean(value)
159
+ when /Publisher/
160
+ book_data[:publisher] = value
161
+ when /Publication data/
162
+ value =~ /\d{2}.\d{2}.(\d{4})/
163
+ year = Regexp.last_match[1]
164
+ book_data[:publisher] = year
165
+ end
177
166
  end
178
- book_data[:authors] = authors
179
167
 
180
- publisher_item = doc % 'li.publisher'
181
- if publisher_item
182
- publisher_item.inner_text =~ /Publisher:\s*(.+)/
183
- book_data[:publisher] = Regexp.last_match[1]
168
+ meta = doc / 'meta'
169
+ meta.each do |it|
170
+ attrs = it.attributes
171
+ property = attrs['property']
172
+ next unless property
173
+ case property
174
+ when 'og:title'
175
+ book_data[:title] = attrs['content']
176
+ when 'og:image'
177
+ book_data[:image_url] = attrs['content']
178
+ end
184
179
  end
185
180
 
186
- date_item = doc % 'li.pubDate'
187
- if date_item
188
- date_item.inner_text =~ /Date: ([^\s]*)\s*([\d]{4})/
189
- year = Regexp.last_match[2].to_i if Regexp.last_match[2]
190
- book_data[:publication_year] = year
191
- end
181
+ author_links = doc / 'span.contributors a'
182
+ authors = author_links.map(&:inner_text)
183
+ book_data[:authors] = authors
192
184
 
193
185
  book_data[:binding] = ''
194
- format_list_items = doc / '//div.col-one/ul/li'
195
- format_list_items.each do |li|
196
- if li.inner_text =~ /Format:\s*(.*),/
197
- book_data[:binding] = Regexp.last_match[1]
198
- end
199
- end
200
-
201
- product_image_div = doc % 'div#product-image'
202
- if product_image_div
203
- images = product_image_div / 'img'
204
- if images.size == 1
205
- book_data[:image_url] = images.first['src']
206
- else
207
- if images.first['src'] =~ /see_inside.gif/
208
- # the first image is the "See Inside!" label
209
- book_data[:image_url] = images[1]['src']
210
- else
211
- book_data[:image_url] = images.first['src']
212
- end
213
- end
186
+ selected_format = (doc / '#availableFormats li.selected a.tabTitle').first
187
+ if selected_format
188
+ book_data[:binding] = selected_format.inner_text
214
189
  end
215
190
 
216
191
  book = Book.new(book_data[:title], book_data[:authors],