alexandria-book-collection-manager 0.6.9.pre1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (293) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/.rubocop.yml +68 -0
  4. data/.rubocop_todo.yml +200 -0
  5. data/CHANGELOG.md +23 -0
  6. data/COPYING +339 -0
  7. data/ChangeLog.0 +3598 -0
  8. data/Gemfile +9 -0
  9. data/INSTALL.rdoc +156 -0
  10. data/PACKAGING +36 -0
  11. data/README.md +88 -0
  12. data/RELEASE_CHECKLIST +18 -0
  13. data/Rakefile +264 -0
  14. data/TODO +24 -0
  15. data/alexandria-book-collection-manager.gemspec +35 -0
  16. data/alexandria.desktop.in +10 -0
  17. data/bin/alexandria +82 -0
  18. data/doc/AUTHORS +60 -0
  19. data/doc/BUGS +31 -0
  20. data/doc/FAQ +369 -0
  21. data/doc/HACKING +19 -0
  22. data/doc/NEWS +341 -0
  23. data/doc/alexandria.1 +120 -0
  24. data/doc/cuecat_support.rdoc +67 -0
  25. data/lib/alexandria.rb +85 -0
  26. data/lib/alexandria/about.rb +82 -0
  27. data/lib/alexandria/book_providers.rb +411 -0
  28. data/lib/alexandria/book_providers/adlibris.rb +235 -0
  29. data/lib/alexandria/book_providers/amazon_aws.rb +261 -0
  30. data/lib/alexandria/book_providers/amazon_ecs_util.rb +405 -0
  31. data/lib/alexandria/book_providers/barnes_and_noble.rb +229 -0
  32. data/lib/alexandria/book_providers/bol_it.rb +162 -0
  33. data/lib/alexandria/book_providers/deastore.rb +277 -0
  34. data/lib/alexandria/book_providers/douban.rb +135 -0
  35. data/lib/alexandria/book_providers/ibs_it.rb +149 -0
  36. data/lib/alexandria/book_providers/mcu.rb +177 -0
  37. data/lib/alexandria/book_providers/proxis.rb +205 -0
  38. data/lib/alexandria/book_providers/pseudomarc.rb +185 -0
  39. data/lib/alexandria/book_providers/renaud.rb +142 -0
  40. data/lib/alexandria/book_providers/siciliano.rb +271 -0
  41. data/lib/alexandria/book_providers/thalia.rb +197 -0
  42. data/lib/alexandria/book_providers/web.rb +59 -0
  43. data/lib/alexandria/book_providers/webster_it.rb +173 -0
  44. data/lib/alexandria/book_providers/worldcat.rb +251 -0
  45. data/lib/alexandria/book_providers/z3950.rb +422 -0
  46. data/lib/alexandria/config.rb +8 -0
  47. data/lib/alexandria/console.rb +31 -0
  48. data/lib/alexandria/execution_queue.rb +96 -0
  49. data/lib/alexandria/export_library.rb +536 -0
  50. data/lib/alexandria/import_library.rb +316 -0
  51. data/lib/alexandria/import_library_csv.rb +270 -0
  52. data/lib/alexandria/logging.rb +159 -0
  53. data/lib/alexandria/models/book.rb +72 -0
  54. data/lib/alexandria/models/library.rb +714 -0
  55. data/lib/alexandria/net.rb +53 -0
  56. data/lib/alexandria/preferences.rb +324 -0
  57. data/lib/alexandria/scanners.rb +42 -0
  58. data/lib/alexandria/scanners/cuecat.rb +118 -0
  59. data/lib/alexandria/scanners/keyboard.rb +57 -0
  60. data/lib/alexandria/smart_library.rb +525 -0
  61. data/lib/alexandria/ui.rb +53 -0
  62. data/lib/alexandria/ui/builder_base.rb +43 -0
  63. data/lib/alexandria/ui/callbacks.rb +389 -0
  64. data/lib/alexandria/ui/completion_models.rb +228 -0
  65. data/lib/alexandria/ui/dialogs/about_dialog.rb +59 -0
  66. data/lib/alexandria/ui/dialogs/acquire_dialog.rb +640 -0
  67. data/lib/alexandria/ui/dialogs/alert_dialog.rb +68 -0
  68. data/lib/alexandria/ui/dialogs/bad_isbns_dialog.rb +43 -0
  69. data/lib/alexandria/ui/dialogs/barcode_animation.rb +159 -0
  70. data/lib/alexandria/ui/dialogs/book_properties_dialog.rb +210 -0
  71. data/lib/alexandria/ui/dialogs/book_properties_dialog_base.rb +432 -0
  72. data/lib/alexandria/ui/dialogs/export_dialog.rb +172 -0
  73. data/lib/alexandria/ui/dialogs/import_dialog.rb +205 -0
  74. data/lib/alexandria/ui/dialogs/misc_dialogs.rb +85 -0
  75. data/lib/alexandria/ui/dialogs/new_book_dialog.rb +639 -0
  76. data/lib/alexandria/ui/dialogs/new_book_dialog_manual.rb +153 -0
  77. data/lib/alexandria/ui/dialogs/new_smart_library_dialog.rb +67 -0
  78. data/lib/alexandria/ui/dialogs/preferences_dialog.rb +587 -0
  79. data/lib/alexandria/ui/dialogs/smart_library_properties_dialog.rb +56 -0
  80. data/lib/alexandria/ui/dialogs/smart_library_properties_dialog_base.rb +432 -0
  81. data/lib/alexandria/ui/dndable.rb +81 -0
  82. data/lib/alexandria/ui/gtk_thread_help.rb +88 -0
  83. data/lib/alexandria/ui/icons.rb +100 -0
  84. data/lib/alexandria/ui/iconview.rb +91 -0
  85. data/lib/alexandria/ui/iconview_tooltips.rb +162 -0
  86. data/lib/alexandria/ui/init.rb +93 -0
  87. data/lib/alexandria/ui/libraries_combo.rb +75 -0
  88. data/lib/alexandria/ui/listview.rb +310 -0
  89. data/lib/alexandria/ui/main_app.rb +67 -0
  90. data/lib/alexandria/ui/multi_drag_treeview.rb +150 -0
  91. data/lib/alexandria/ui/sidepane.rb +194 -0
  92. data/lib/alexandria/ui/sound.rb +107 -0
  93. data/lib/alexandria/ui/ui_manager.rb +1308 -0
  94. data/lib/alexandria/undo_manager.rb +78 -0
  95. data/lib/alexandria/utils.rb +30 -0
  96. data/lib/alexandria/version.rb +24 -0
  97. data/lib/alexandria/web_themes.rb +75 -0
  98. data/misc/sounds/README +15 -0
  99. data/misc/sounds/bad_scan.csd +62 -0
  100. data/misc/sounds/good_scan.csd +61 -0
  101. data/misc/sounds/scanning.csd +46 -0
  102. data/po/ChangeLog +488 -0
  103. data/po/Makefile +44 -0
  104. data/po/README +29 -0
  105. data/po/commit-po +72 -0
  106. data/po/cs.po +1437 -0
  107. data/po/cy.po +1521 -0
  108. data/po/de.po +1400 -0
  109. data/po/el.po +1379 -0
  110. data/po/es.po +1376 -0
  111. data/po/fr.po +1420 -0
  112. data/po/ga.po +1359 -0
  113. data/po/gl.po +1397 -0
  114. data/po/it.po +1406 -0
  115. data/po/ja.po +1355 -0
  116. data/po/mk.po +1373 -0
  117. data/po/nb.po +1386 -0
  118. data/po/nl.po +1405 -0
  119. data/po/pl.po +1373 -0
  120. data/po/pt.po +1398 -0
  121. data/po/pt_BR.po +1409 -0
  122. data/po/ru.po +1372 -0
  123. data/po/sk.po +1380 -0
  124. data/po/sv.po +1402 -0
  125. data/po/uk.po +1423 -0
  126. data/po/zh_TW.po +1394 -0
  127. data/schemas/alexandria.schemas +300 -0
  128. data/share/alexandria/glade/acquire_dialog__builder.glade +201 -0
  129. data/share/alexandria/glade/book_properties_dialog__builder.glade +910 -0
  130. data/share/alexandria/glade/main_app__builder.glade +229 -0
  131. data/share/alexandria/glade/new_book_dialog__builder.glade +379 -0
  132. data/share/alexandria/glade/preferences_dialog__builder.glade +733 -0
  133. data/share/alexandria/icons/alexandria.png +0 -0
  134. data/share/alexandria/icons/alexandria_small.png +0 -0
  135. data/share/alexandria/icons/book.png +0 -0
  136. data/share/alexandria/icons/book_icon.png +0 -0
  137. data/share/alexandria/icons/book_small.png +0 -0
  138. data/share/alexandria/icons/cuecat.png +0 -0
  139. data/share/alexandria/icons/cuecat_inactive.png +0 -0
  140. data/share/alexandria/icons/favorite_tag.png +0 -0
  141. data/share/alexandria/icons/less.png +0 -0
  142. data/share/alexandria/icons/library.png +0 -0
  143. data/share/alexandria/icons/library_small.png +0 -0
  144. data/share/alexandria/icons/lookup.png +0 -0
  145. data/share/alexandria/icons/more.png +0 -0
  146. data/share/alexandria/icons/no_cover.png +0 -0
  147. data/share/alexandria/icons/smart_library.png +0 -0
  148. data/share/alexandria/icons/smart_library_small.png +0 -0
  149. data/share/alexandria/icons/star_set.png +0 -0
  150. data/share/alexandria/icons/star_unset.png +0 -0
  151. data/share/alexandria/icons/view_as_icons.png +0 -0
  152. data/share/alexandria/icons/view_as_list.png +0 -0
  153. data/share/alexandria/ui/menus.xml +91 -0
  154. data/share/alexandria/ui/popups.xml +91 -0
  155. data/share/alexandria/web-themes/clean/clean.css +85 -0
  156. data/share/alexandria/web-themes/clean/preview.jpg +0 -0
  157. data/share/alexandria/web-themes/list/list.css +105 -0
  158. data/share/alexandria/web-themes/list/preview.jpg +0 -0
  159. data/share/app-icon/16x16/alexandria.png +0 -0
  160. data/share/app-icon/16x16/alexandria.svg +263 -0
  161. data/share/app-icon/22x22/alexandria.png +0 -0
  162. data/share/app-icon/22x22/alexandria.svg +465 -0
  163. data/share/app-icon/24x24/alexandria.png +0 -0
  164. data/share/app-icon/32x32/alexandria.png +0 -0
  165. data/share/app-icon/32x32/alexandria.svg +813 -0
  166. data/share/app-icon/32x32/alexandria.xpm +241 -0
  167. data/share/app-icon/48x48/alexandria.png +0 -0
  168. data/share/app-icon/scalable/alexandria.svg +700 -0
  169. data/share/gnome/help/alexandria/C/about.xml +44 -0
  170. data/share/gnome/help/alexandria/C/adding-books.xml +339 -0
  171. data/share/gnome/help/alexandria/C/alexandria.xml +185 -0
  172. data/share/gnome/help/alexandria/C/bugs.xml +18 -0
  173. data/share/gnome/help/alexandria/C/editing-book-properties.xml +124 -0
  174. data/share/gnome/help/alexandria/C/exporting.xml +81 -0
  175. data/share/gnome/help/alexandria/C/figures/adding_books_acquire_from_scanner_process.png +0 -0
  176. data/share/gnome/help/alexandria/C/figures/adding_books_add_by_isbn.png +0 -0
  177. data/share/gnome/help/alexandria/C/figures/adding_books_isbn_import.png +0 -0
  178. data/share/gnome/help/alexandria/C/figures/adding_books_manual_details.png +0 -0
  179. data/share/gnome/help/alexandria/C/figures/adding_books_rename_library_after_import.png +0 -0
  180. data/share/gnome/help/alexandria/C/figures/adding_books_search_results.png +0 -0
  181. data/share/gnome/help/alexandria/C/figures/editing_book_properties_info.png +0 -0
  182. data/share/gnome/help/alexandria/C/figures/editing_book_properties_loaning.png +0 -0
  183. data/share/gnome/help/alexandria/C/figures/exporting_information_html.png +0 -0
  184. data/share/gnome/help/alexandria/C/figures/getting_started_first_launched.png +0 -0
  185. data/share/gnome/help/alexandria/C/figures/searching_filtering_views_list_view.png +0 -0
  186. data/share/gnome/help/alexandria/C/figures/searching_filtering_views_list_view_search.png +0 -0
  187. data/share/gnome/help/alexandria/C/figures/settings_providers_new_z3950.png +0 -0
  188. data/share/gnome/help/alexandria/C/figures/smart_libraries_new_smart_library.png +0 -0
  189. data/share/gnome/help/alexandria/C/figures/working_with_libraries_library_pane.png +0 -0
  190. data/share/gnome/help/alexandria/C/getting-started.xml +154 -0
  191. data/share/gnome/help/alexandria/C/gnu-fdl-1.2.xml +543 -0
  192. data/share/gnome/help/alexandria/C/introduction.xml +142 -0
  193. data/share/gnome/help/alexandria/C/searching.xml +90 -0
  194. data/share/gnome/help/alexandria/C/settings.xml +140 -0
  195. data/share/gnome/help/alexandria/C/smart-libraries.xml +160 -0
  196. data/share/gnome/help/alexandria/C/working-with-libraries.xml +76 -0
  197. data/share/gnome/help/alexandria/ChangeLog +99 -0
  198. data/share/gnome/help/alexandria/fr/alexandria.xml +2292 -0
  199. data/share/gnome/help/alexandria/fr/figures/alexandria_add_button.png +0 -0
  200. data/share/gnome/help/alexandria/fr/figures/alexandria_add_by_isbn_1.png +0 -0
  201. data/share/gnome/help/alexandria/fr/figures/alexandria_add_by_search_1.png +0 -0
  202. data/share/gnome/help/alexandria/fr/figures/alexandria_add_manually.png +0 -0
  203. data/share/gnome/help/alexandria/fr/figures/alexandria_add_z3950.png +0 -0
  204. data/share/gnome/help/alexandria/fr/figures/alexandria_close_button.png +0 -0
  205. data/share/gnome/help/alexandria/fr/figures/alexandria_edit_info.png +0 -0
  206. data/share/gnome/help/alexandria/fr/figures/alexandria_export_web_page.png +0 -0
  207. data/share/gnome/help/alexandria/fr/figures/alexandria_importing.png +0 -0
  208. data/share/gnome/help/alexandria/fr/figures/alexandria_library_pane.png +0 -0
  209. data/share/gnome/help/alexandria/fr/figures/alexandria_list_view.png +0 -0
  210. data/share/gnome/help/alexandria/fr/figures/alexandria_list_view_search.png +0 -0
  211. data/share/gnome/help/alexandria/fr/figures/alexandria_loaning.png +0 -0
  212. data/share/gnome/help/alexandria/fr/figures/alexandria_main_window.png +0 -0
  213. data/share/gnome/help/alexandria/fr/figures/alexandria_remove_button.png +0 -0
  214. data/share/gnome/help/alexandria/ja/about.xml +33 -0
  215. data/share/gnome/help/alexandria/ja/adding-books.xml +314 -0
  216. data/share/gnome/help/alexandria/ja/alexandria.xml +172 -0
  217. data/share/gnome/help/alexandria/ja/bugs.xml +11 -0
  218. data/share/gnome/help/alexandria/ja/editing-book-properties.xml +100 -0
  219. data/share/gnome/help/alexandria/ja/exporting.xml +98 -0
  220. data/share/gnome/help/alexandria/ja/figures/adding_books_acquire_from_scanner_process.png +0 -0
  221. data/share/gnome/help/alexandria/ja/figures/adding_books_add_by_isbn.png +0 -0
  222. data/share/gnome/help/alexandria/ja/figures/adding_books_isbn_import.png +0 -0
  223. data/share/gnome/help/alexandria/ja/figures/adding_books_manual_details.png +0 -0
  224. data/share/gnome/help/alexandria/ja/figures/adding_books_rename_library_after_import.png +0 -0
  225. data/share/gnome/help/alexandria/ja/figures/adding_books_search_results.png +0 -0
  226. data/share/gnome/help/alexandria/ja/figures/editing_book_properties_info.png +0 -0
  227. data/share/gnome/help/alexandria/ja/figures/editing_book_properties_loaning.png +0 -0
  228. data/share/gnome/help/alexandria/ja/figures/exporting_information_html.png +0 -0
  229. data/share/gnome/help/alexandria/ja/figures/getting_started_first_launched.png +0 -0
  230. data/share/gnome/help/alexandria/ja/figures/searching_filtering_views_list_view.png +0 -0
  231. data/share/gnome/help/alexandria/ja/figures/searching_filtering_views_list_view_search.png +0 -0
  232. data/share/gnome/help/alexandria/ja/figures/settings_providers_new_z3950.png +0 -0
  233. data/share/gnome/help/alexandria/ja/figures/smart_libraries_new_smart_library.png +0 -0
  234. data/share/gnome/help/alexandria/ja/figures/working_with_libraries_library_pane.png +0 -0
  235. data/share/gnome/help/alexandria/ja/getting-started.xml +144 -0
  236. data/share/gnome/help/alexandria/ja/gnu-fdl-1.2.xml +541 -0
  237. data/share/gnome/help/alexandria/ja/introduction.xml +134 -0
  238. data/share/gnome/help/alexandria/ja/searching.xml +104 -0
  239. data/share/gnome/help/alexandria/ja/settings.xml +129 -0
  240. data/share/gnome/help/alexandria/ja/smart-libraries.xml +140 -0
  241. data/share/gnome/help/alexandria/ja/working-with-libraries.xml +88 -0
  242. data/share/menu/alexandria +7 -0
  243. data/share/omf/alexandria/alexandria-C.omf.in +27 -0
  244. data/share/omf/alexandria/alexandria-fr.omf.in +30 -0
  245. data/share/sounds/alexandria/bad_scan.ogg +0 -0
  246. data/share/sounds/alexandria/bad_scan.wav +0 -0
  247. data/share/sounds/alexandria/good_scan.ogg +0 -0
  248. data/share/sounds/alexandria/good_scan.wav +0 -0
  249. data/share/sounds/alexandria/scanning.ogg +0 -0
  250. data/share/sounds/alexandria/scanning.wav +0 -0
  251. data/spec/alexandria/library_spec.rb +205 -0
  252. data/spec/alexandria/preferences_spec.rb +22 -0
  253. data/spec/alexandria/scanners/cuecat_spec.rb +68 -0
  254. data/spec/alexandria/smart_library_spec.rb +22 -0
  255. data/spec/alexandria/ui/dialogs_spec.rb +90 -0
  256. data/spec/alexandria/ui/iconview_spec.rb +27 -0
  257. data/spec/alexandria/ui/listview_spec.rb +28 -0
  258. data/spec/alexandria/ui/main_app_spec.rb +48 -0
  259. data/spec/alexandria/ui/sidepane_spec.rb +27 -0
  260. data/spec/alexandria/ui/ui_manager_spec.rb +26 -0
  261. data/spec/alexandria/ui/ui_utilities_spec.rb +60 -0
  262. data/spec/alexandria/utilities_spec.rb +50 -0
  263. data/spec/data/libraries/0.6.1-noisbn/My Library/0201398257.yaml +10 -0
  264. data/spec/data/libraries/0.6.1-noisbn/My Library/1565920007.yaml +10 -0
  265. data/spec/data/libraries/0.6.1/My Library/0192812173.yaml +9 -0
  266. data/spec/data/libraries/0.6.1/My Library/0201398257.cover +0 -0
  267. data/spec/data/libraries/0.6.1/My Library/0201398257.yaml +10 -0
  268. data/spec/data/libraries/0.6.1/My Library/1565920007.yaml +10 -0
  269. data/spec/data/libraries/0.6.2/My Library/9780140266146.cover +0 -0
  270. data/spec/data/libraries/0.6.2/My Library/9780140266146.yaml +16 -0
  271. data/spec/data/libraries/0.6.2/My Library/9780140278781.cover +0 -0
  272. data/spec/data/libraries/0.6.2/My Library/9780140278781.yaml +21 -0
  273. data/spec/data/libraries/0.6.2/My Library/9780571147168.cover +0 -0
  274. data/spec/data/libraries/0.6.2/My Library/9780571147168.yaml +20 -0
  275. data/spec/data/libraries/0.6.2/My Library/9780575079038.cover +0 -0
  276. data/spec/data/libraries/0.6.2/My Library/9780575079038.yaml +20 -0
  277. data/spec/data/libraries/0.6.2/My Library/9780755322800.cover +0 -0
  278. data/spec/data/libraries/0.6.2/My Library/9780755322800.yaml +20 -0
  279. data/spec/spec_helper.rb +46 -0
  280. data/tasks/rdoc.rake +6 -0
  281. data/tasks/setup.rb +30 -0
  282. data/tasks/spec.rake +29 -0
  283. data/tasks/test.rake +38 -0
  284. data/test/application_test.rb +39 -0
  285. data/test/book_test.rb +34 -0
  286. data/test/data/isbns.txt +3 -0
  287. data/test/isbn_test.rb +68 -0
  288. data/test/providers_test.rb +254 -0
  289. data/test/test_helper.rb +42 -0
  290. data/util/rake/fileinstall.rb +313 -0
  291. data/util/rake/gettextgenerate.rb +158 -0
  292. data/util/rake/omfgenerate.rb +79 -0
  293. metadata +452 -0
@@ -0,0 +1,67 @@
1
+ = CueCat Barcode Scanner Support
2
+
3
+ Alexandria now supports the use of unmodified CueCat barcode scanners
4
+ for scanning ISBN barcodes.
5
+
6
+
7
+ == What is a CueCat?
8
+
9
+ The CueCat is a wand-style barcode scanner, available with either a
10
+ PS/2 keyboard connector (and a PS/2 keyboard pass-through), or with a
11
+ standard keyboard-type USB connector.
12
+
13
+ You scan a barcode by placing the 'nose' of the scanner on one side of
14
+ the barcode, in the clear area before the first black bar, and waiting
15
+ until the flashing red light stops flashing and illuminates the
16
+ surface brightly, then swiftly passing the scanner across the barcode
17
+ and into the clear area on the far side. The scan data is then input
18
+ into your computer as if through a keyboard. You can test the
19
+ operation of your CueCat by placing your keyboard cursor inside any
20
+ text editor and scanning a barcode.
21
+
22
+ By default the scan data is scrambled. There are instructions, widely
23
+ available on the Internet, detailing how to modify the circuit board to
24
+ disable scrambling, but some barcode scanning applications, including
25
+ Alexandria, can automatically decode scrambled data from a CueCat.
26
+
27
+
28
+ == The History of CueCat?
29
+
30
+ Millions of CueCat scanners were distributed free of charge by their
31
+ creator the Digital Convergence Corporation in the late 1990s and
32
+ early 2000s. The intention was that customers would use the scanners
33
+ to scan special barcodes in magazine advertisments and articles, and
34
+ software would take the customers to a related web page.
35
+
36
+ Dozens of eager hackers, upon recieving these barcode scanners,
37
+ immediately set about finding a way to use them for a wider variety of
38
+ purposes, such as book cataloging. They decoded the very simple
39
+ scrambling of the scanner output data (it was a form of Base64
40
+ encoding), and even reported on how to lift a single microchip pin to
41
+ circumvent the scrambling in hardware. They also discovered that each
42
+ CueCat has a unique serial number (which could have allowed
43
+ DigitalConvergence to build up profiles of the products customers were
44
+ interested in) and showed how to zero-out or avoid the serial number.
45
+
46
+ Digital Convergence attempted to restrict such activities, but
47
+ eventually the company went bust, leaving several electronics
48
+ warehouses to buy up hundreds of thousands of unshipped CueCats in the
49
+ ensuing liquidation sale.
50
+
51
+ Today, PS/2 and USB CueCat scanners are available cheaply over the
52
+ internet (about US$12 in 2007). Apart from the slight inconvenience
53
+ caused by the data scrambling, they are eminently usable as cheap,
54
+ robust, wand-style barcode scanners.
55
+
56
+ == The Old Linux Driver
57
+
58
+ A kernel driver for the CueCat was developed as a patch for the 2.4
59
+ Linux kernel series. This provided decoding of the scrambled input,
60
+ and made input data available from <tt>/dev/scanners/cuecat</tt>.
61
+
62
+ Since this driver is currently unmaintained and does not work with 2.6
63
+ kernels, support for this has been dropped from Alexandria. However,
64
+ since Alexandria 0.6.2, there is so-called 'userland' support for the
65
+ CueCat, which simply means that modifications to the kernel are not
66
+ necessary. Instead, the decoding of the simple data scrambling is done
67
+ by the application software.
@@ -0,0 +1,85 @@
1
+ # Copyright (C) 2004-2006 Laurent Sansonetti
2
+ # Modifications Copyright (C) 2011 Matijs van Zuijlen
3
+ #
4
+ # Alexandria is free software; you can redistribute it and/or
5
+ # modify it under the terms of the GNU General Public License as
6
+ # published by the Free Software Foundation; either version 2 of the
7
+ # License, or (at your option) any later version.
8
+ #
9
+ # Alexandria is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
+ # General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public
15
+ # License along with Alexandria; see the file COPYING. If not,
16
+ # write to the Free Software Foundation, Inc., 51 Franklin Street,
17
+ # Fifth Floor, Boston, MA 02110-1301 USA.
18
+
19
+ require 'gettext'
20
+
21
+ # XXX: Gettext hotfix
22
+ module Gem
23
+ class << self
24
+ undef :all_load_paths if method_defined? :all_load_paths
25
+ def all_load_paths
26
+ []
27
+ end
28
+ end
29
+ end
30
+
31
+ require 'logger'
32
+ require 'alexandria/logging'
33
+ require 'alexandria/about'
34
+
35
+ module Alexandria
36
+ def self.set_proxy
37
+ ENV['http_proxy'] = nil if !ENV['http_proxy'].nil? and URI.parse(ENV['http_proxy']).userinfo.nil?
38
+ end
39
+
40
+ def self.set_log_level
41
+ if $DEBUG
42
+ Alexandria.log.level = Logger::DEBUG
43
+ end
44
+ Alexandria.log.debug { 'Initializing Alexandria...' }
45
+ end
46
+
47
+ def self.main
48
+ set_proxy
49
+ set_log_level
50
+ Alexandria::UI.main
51
+ end
52
+ end
53
+
54
+ # lrz says 'macui' is obsolete (may be supported again some day)
55
+ # unless $MACOSX
56
+ require 'alexandria/config'
57
+ require 'alexandria/version'
58
+
59
+ # else
60
+ # module Alexandria
61
+ # module Config
62
+ # DATA_DIR = OSX::NSBundle.mainBundle.resourcePath.fileSystemRepresentation
63
+ # end
64
+ # VERSION = OSX::NSBundle.mainBundle.infoDictionary.objectForKey('CFBundleVersion').to_s
65
+ # end
66
+ # end
67
+ require 'alexandria/utils'
68
+
69
+ require 'alexandria/models/book'
70
+ require 'alexandria/models/library'
71
+
72
+ require 'alexandria/smart_library'
73
+ require 'alexandria/execution_queue'
74
+ require 'alexandria/import_library'
75
+ require 'alexandria/export_library'
76
+ require 'alexandria/book_providers'
77
+ require 'alexandria/preferences'
78
+ require 'alexandria/undo_manager'
79
+ require 'alexandria/web_themes'
80
+
81
+ # lrz says 'macui' is obsolete (may be supported again some day)
82
+ # require $MACOSX ? 'alexandria/macui' : 'alexandria/ui'
83
+
84
+ require 'alexandria/ui'
85
+ require 'alexandria/console'
@@ -0,0 +1,82 @@
1
+ # encoding: utf-8
2
+ # Copyright (C) 2004-2006 Laurent Sansonetti
3
+ # Copyright (C) 2008 Joseph Method
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.
19
+
20
+ module Alexandria
21
+ TITLE = 'Alexandria'
22
+ TEXTDOMAIN = 'alexandria'
23
+ extend GetText
24
+ bindtextdomain(Alexandria::TEXTDOMAIN, charset: 'UTF-8')
25
+ DESCRIPTION = _('A program to help you manage your book collection.')
26
+ COPYRIGHT = "Copyright (C) 2004,2005,2006 Laurent Sansonetti\n" \
27
+ 'Copyright (C) 2007,2008,2009,2010 Alexandria Contributors'
28
+ AUTHORS = [
29
+ 'Alexander McCormmach <alexander@tunicate.org>',
30
+ 'Aymeric Nys <aymeric@nnx.com>',
31
+ 'Cathal Mc Ginley <cathal.alexandria@gnostai.org>',
32
+ 'Claudio Belotti <bel8@lilik.it>',
33
+ 'Constantine Evans <cevans@costinet.org>',
34
+ 'Dafydd Harries <daf@muse.19inch.net>',
35
+ 'Javier Fernandez-Sanguino Pena <jfs@debian.org>',
36
+ 'Joseph Method <tristil@gmail.com>',
37
+ 'Kevin Schultz <schultkl@ieee.org>',
38
+ 'Laurent Sansonetti <lrz@gnome.org>',
39
+ 'Marco Costantini <costanti@science.unitn.it>',
40
+ 'Mathieu Leduc-Hamel <arrak@arrak.org>',
41
+ 'Owain Evans <o.evans@gmail.com>',
42
+ 'Pascal Terjan <pterjan@linuxfr.org>',
43
+ 'Rene Samselnig <sandman@sdm-net.org>',
44
+ 'Robby Stephenson <robby@periapsis.org>',
45
+ 'Sun Ning <classicning@gmail.com>',
46
+ 'Takayuki Kusano <AE5T-KSN@asahi-net.or.jp>',
47
+ 'Timothy Malone <timothy.malone@gmail.com>',
48
+ 'Zachary P. Landau <kapheine@hypa.net>'
49
+ ]
50
+ DOCUMENTERS = [
51
+ 'Cathal Mc Ginley <cathal.alexandria@gnostai.org>',
52
+ 'Liam Davison <registrations@liamjdavison.info>'
53
+ ]
54
+ TRANSLATORS = [
55
+ 'Adrián Chaves Fernández <adriyetichaves@gmail.com> (gl)',
56
+ 'Cathal Mc Ginley <cathal.alexandria@gnostai.org> (ga)',
57
+ 'CHIKAMA Masaki <masaki.chikama@gmail.com> (ja)',
58
+ 'Dafydd Harries <daf@muse.19inch.net> (cy)',
59
+ 'Damjan Dimitrioski <damjandimitrioski@gmail.com> (mk)',
60
+ 'Giacomo Margarito <giacomomargarito@gmail.com> (it)',
61
+ 'Jack Myrseh <jack@enkom.no> (nb)',
62
+ 'Joachim Breitner <mail@joachim-breitner.de> (de)',
63
+ 'José Ling <jlgdot369@gmail.com> (zh_TW)',
64
+ 'Lennart Karssen <lennart@karssen.org> (nl)',
65
+ 'Lígia Moreira <ligia.moreira@netvisao.pt> (fr, pt, pt_BR)',
66
+ 'Martin Karlsson <martinkarlsson81@hotmail.com> (sv)',
67
+ 'Michael Kotsarinis <mkotsari1@pre.forthnet.gr> (el)',
68
+ 'Miguel Ángel García <magmax@ieee.org> (es)',
69
+ 'Peter Kováč <kovac.peter@fotopriestor.sk> (sk)',
70
+ 'Petr Vanek <vanous@penguin.cz> (cs)',
71
+ 'Piotr Drąg <piotrdrag@gmail.com> (pl)',
72
+ 'Serhij Dubyk <dubyk@library.lviv.ua> (uk)'
73
+ ]
74
+ ARTISTS = [
75
+ 'Andreas Nilsson <nisses.mail@home.se>',
76
+ 'Stefanie Dijoux <stefanie.dijoux@gmail.com>'
77
+ ]
78
+ LIST = 'alexandria-list@rubyforge.org'
79
+ BUGREPORT_URL = 'http://rubyforge.org/tracker/?func=add&group_id=205&atid=863'
80
+ WEBSITE_URL = 'http://alexandria.rubyforge.org'
81
+ DONATE_URL = ''
82
+ end
@@ -0,0 +1,411 @@
1
+ # Copyright (C) 2004-2006 Laurent Sansonetti
2
+ # Copyright (C) 2009 Cathal Mc Ginley
3
+ # Copyright (C) 2011, 2014 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.
19
+
20
+ require 'singleton'
21
+ require 'observer'
22
+
23
+ module Alexandria
24
+ # FIXME: Use delegation instead of inheritance.
25
+ class BookProviders < Array
26
+ include Logging
27
+ include Singleton
28
+ include Observable
29
+ include GetText
30
+ GetText.bindtextdomain(Alexandria::TEXTDOMAIN, charset: 'UTF-8')
31
+
32
+ SEARCH_BY_ISBN, SEARCH_BY_TITLE, SEARCH_BY_AUTHORS,
33
+ SEARCH_BY_KEYWORD = (0..3).to_a
34
+
35
+ class SearchError < StandardError; end
36
+ class NoResultsError < SearchError; end
37
+ class ProviderSkippedError < NoResultsError; end # not an error :^(
38
+ class SearchEmptyError < SearchError; end # sigh, again not really an error
39
+ class TooManyResultsError < SearchError; end
40
+ class InvalidSearchTypeError < SearchError; end
41
+
42
+ def self.search(criterion, type)
43
+ factory_n = 0
44
+ # puts "book_providers search #{self.instance.count_observers}"
45
+
46
+ begin
47
+ factory = instance[factory_n]
48
+ puts factory.fullname + ' lookup' if $DEBUG
49
+ unless factory.enabled
50
+ puts factory.fullname + ' disabled!, skipping...' if $DEBUG
51
+ raise ProviderSkippedError
52
+ end
53
+ instance.changed
54
+ instance.notify_observers(:searching, factory.fullname) # new
55
+ results = factory.search(criterion, type)
56
+
57
+ # sanity check if at least one valid result is actually found
58
+ results.delete_if { |book, _cover| book.nil? }
59
+
60
+ if results.length == 0
61
+ instance.changed
62
+ instance.notify_observers(:not_found, factory.fullname) # new
63
+ raise NoResultsError
64
+ else
65
+ log.info { 'found at ' + factory.fullname }
66
+ instance.changed
67
+ instance.notify_observers(:found, factory.fullname) # new
68
+ return results
69
+ end
70
+ rescue => boom
71
+ if boom.is_a? NoResultsError
72
+ unless boom.instance_of? ProviderSkippedError
73
+ instance.changed
74
+ instance.notify_observers(:not_found, factory.fullname) # new
75
+ Thread.new { sleep(0.5) }.join
76
+ end
77
+ else
78
+ instance.changed
79
+ instance.notify_observers(:error, factory.fullname) # new
80
+ Thread.new { sleep(0.5) }.join # hrmmmm, to make readable...
81
+ trace = boom.backtrace.join("\n >")
82
+ log.warn { "Provider #{factory.name} encountered error: #{boom.message} #{trace}" }
83
+ end
84
+ if last == factory
85
+ log.warn { "Error while searching #{criterion}" }
86
+ message = case boom
87
+ when Timeout::Error
88
+ _("Couldn't reach the provider '%s': timeout " \
89
+ 'expired.') % factory.name
90
+
91
+ when SocketError
92
+ _("Couldn't reach the provider '%s': socket " \
93
+ 'error (%s).') % [factory.name, boom.message]
94
+
95
+ when NoResultsError
96
+ _('No results were found. Make sure your ' \
97
+ 'search criterion is spelled correctly, and ' \
98
+ 'try again.')
99
+
100
+ when ProviderSkippedError
101
+ _('No results were found. Make sure your ' \
102
+ 'search criterion is spelled correctly, and ' \
103
+ 'try again.')
104
+
105
+ when TooManyResultsError
106
+ _('Too many results for that search.')
107
+
108
+ when InvalidSearchTypeError
109
+ _('Invalid search type.')
110
+
111
+ else
112
+ boom.message
113
+ end
114
+ puts "raising empty error #{message}"
115
+ raise SearchEmptyError, message
116
+ else
117
+ factory_n += 1
118
+ retry
119
+ end
120
+ end
121
+ end
122
+
123
+ def self.isbn_search(criterion)
124
+ search(criterion, SEARCH_BY_ISBN)
125
+ end
126
+
127
+ class Preferences < Array
128
+ def initialize(provider)
129
+ @provider = provider
130
+ end
131
+
132
+ class Variable
133
+ attr_reader :name, :description,
134
+ :possible_values
135
+ attr_accessor :value
136
+
137
+ def initialize(provider, name, description, default_value,
138
+ possible_values = nil, mandatory = true)
139
+
140
+ @provider = provider
141
+ @name = name
142
+ @description = description
143
+ @value = default_value
144
+ @possible_values = possible_values
145
+ @mandatory = mandatory
146
+ end
147
+
148
+ def default_value=(new_value)
149
+ self.value = new_value
150
+ end
151
+
152
+ def new_value=(new_value)
153
+ message = @provider.variable_name(self) + '='
154
+ Alexandria::Preferences.instance.send(message,
155
+ new_value)
156
+ self.value = new_value
157
+ end
158
+
159
+ def provider_name
160
+ @provider.name.downcase
161
+ end
162
+
163
+ def mandatory?
164
+ @mandatory
165
+ end
166
+ end
167
+
168
+ def add(*args)
169
+ self << Variable.new(@provider, *args)
170
+ end
171
+
172
+ def [](obj)
173
+ case obj
174
+ when String
175
+ var = variable_named(obj)
176
+ var ? var.value : nil
177
+ when Integer
178
+ super(obj)
179
+ end
180
+ end
181
+
182
+ def variable_named(name)
183
+ find { |var| var.name == name }
184
+ end
185
+
186
+ def read
187
+ each do |var|
188
+ message = @provider.variable_name(var)
189
+ val = Alexandria::Preferences.instance.send(message)
190
+ var.value = val unless val.nil? or (val == '' and var.mandatory?)
191
+ end
192
+ end
193
+ end
194
+
195
+ class AbstractProvider
196
+ include GetText
197
+ attr_reader :prefs
198
+ attr_accessor :name, :fullname
199
+
200
+ def initialize(name, fullname = nil)
201
+ @name = name
202
+ @fullname = (fullname or name)
203
+ @prefs = Preferences.new(self)
204
+ @prefs.add('enabled', _('Enabled'), true, [true, false])
205
+ end
206
+
207
+ def enabled
208
+ @prefs['enabled']
209
+ end
210
+
211
+ def toggle_enabled
212
+ old_value = enabled
213
+ @prefs.variable_named('enabled').new_value = !old_value
214
+ end
215
+
216
+ def reinitialize(fullname)
217
+ @name << '_' << fullname.hash.to_s
218
+ @fullname = fullname
219
+ prefs = Alexandria::Preferences.instance
220
+ ary = prefs.abstract_providers
221
+ ary ||= []
222
+ ary << @name
223
+ prefs.abstract_providers = ary
224
+ message = variable_name('name') + '='
225
+ prefs.send(message, @fullname)
226
+ end
227
+
228
+ def remove
229
+ prefs = Alexandria::Preferences.instance
230
+ if (ary = prefs.abstract_providers)
231
+ ary.delete(@name)
232
+ prefs.abstract_providers = ary
233
+ end
234
+ if (ary = prefs.providers_priority) and ary.include?(@name)
235
+ ary.delete(@name)
236
+ prefs.providers_priority = ary
237
+ end
238
+ self.prefs.each do |variable|
239
+ name = variable_name(variable)
240
+ prefs.remove_preference(name)
241
+ end
242
+ name = variable_name('name')
243
+ prefs.remove_preference(name)
244
+ prefs.save!
245
+ end
246
+
247
+ def variable_name(object)
248
+ s = case object
249
+ when String
250
+ object
251
+ when Preferences::Variable
252
+ object.name
253
+ else
254
+ raise
255
+ end
256
+ @name.downcase + '_' + s
257
+ end
258
+
259
+ def transport
260
+ config = Alexandria::Preferences.instance.http_proxy_config
261
+ config ? Net::HTTP.Proxy(*config) : Net::HTTP
262
+ end
263
+
264
+ def abstract?
265
+ self.class.abstract?
266
+ end
267
+
268
+ def self.abstract?
269
+ (!included_modules.include?(Singleton))
270
+ end
271
+
272
+ def <=>(provider)
273
+ fullname <=> provider.fullname
274
+ end
275
+
276
+ def self.unabstract
277
+ include Singleton
278
+ undef_method :reinitialize
279
+ undef_method :name=
280
+ undef_method :fullname=
281
+ undef_method :remove
282
+ end
283
+ end
284
+
285
+ class GenericProvider < AbstractProvider
286
+ unabstract
287
+ end
288
+
289
+ require 'alexandria/book_providers/mcu' # yep, still mostly works !
290
+ require 'alexandria/book_providers/douban' # only requires YAML
291
+
292
+ # require 'alexandria/book_providers/ibs_it'
293
+ # require 'alexandria/book_providers/renaud'
294
+ # require 'alexandria/book_providers/bol_it'
295
+ # require 'alexandria/book_providers/webster_it'
296
+ log.info { 'Not loading IBS, Renaud, BOL, Webster (providers not functional)' }
297
+
298
+ # Amazon AWS (Amazon Associates Web Services) provider, needs hpricot
299
+ require 'alexandria/book_providers/amazon_aws'
300
+
301
+ # Website based providers
302
+ require 'alexandria/book_providers/adlibris'
303
+ require 'alexandria/book_providers/barnes_and_noble'
304
+ require 'alexandria/book_providers/deastore'
305
+ require 'alexandria/book_providers/proxis'
306
+ require 'alexandria/book_providers/siciliano'
307
+ require 'alexandria/book_providers/thalia'
308
+ require 'alexandria/book_providers/worldcat'
309
+
310
+ # Ruby/ZOOM is optional
311
+ begin
312
+ require 'alexandria/book_providers/z3950'
313
+ rescue LoadError
314
+ log.info { "Can't load Ruby/ZOOM, hence Z39.50 and providers Library of Congress, British Library not available" }
315
+ end
316
+
317
+ attr_reader :abstract_classes
318
+
319
+ def initialize
320
+ @prefs = Alexandria::Preferences.instance
321
+ @abstract_classes = []
322
+ update_priority
323
+ end
324
+
325
+ def update_priority
326
+ # This is weird code that sorts through the list of classes brought
327
+ # in by requires and sorts through whether they are 'Abstract' or not,
328
+ # adding their names to @prefs.
329
+
330
+ @abstract_classes.clear
331
+ providers = {}
332
+ self.class.constants.each do |constant|
333
+ md = /(.+)Provider$/.match(constant)
334
+ next unless md
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
340
+
341
+ if klass.abstract?
342
+ @abstract_classes << klass
343
+ else
344
+ providers[md[1]] = klass.instance
345
+ end
346
+ end
347
+ end
348
+ if (ary = @prefs.abstract_providers)
349
+ ary.each do |name|
350
+ md = /^(.+)_/.match(name)
351
+ next unless md
352
+ klass_name = md[1] + 'Provider'
353
+ klass = @abstract_classes.find { |x| x.name.include?(klass_name) }
354
+ next unless klass
355
+ fullname = @prefs.send(name.downcase + '_name')
356
+ next unless fullname
357
+ instance = klass.new
358
+ instance.name = name
359
+ instance.fullname = fullname
360
+ instance.prefs.read
361
+ providers[name] = instance
362
+ end
363
+ end
364
+ clear
365
+ rejig_providers_priority
366
+ priority = (@prefs.providers_priority or [])
367
+ priority.map!(&:strip)
368
+ rest = providers.keys - priority
369
+ priority.each { |pname| self << providers[pname] }
370
+ rest.sort.each { |pname| self << providers[pname] }
371
+ self.compact!
372
+ end
373
+
374
+ # FIXME: Define the handful of methods that use this.
375
+ def self.method_missing(id, *args, &block)
376
+ if instance.respond_to? id
377
+ instance.method(id).call(*args, &block)
378
+ else
379
+ super
380
+ end
381
+ end
382
+
383
+ private
384
+
385
+ def rejig_providers_priority
386
+ priority = (@prefs.providers_priority or [])
387
+ unless priority.empty?
388
+ changed = false
389
+
390
+ if (ecs_index = priority.index('AmazonECS'))
391
+ priority[ecs_index] = 'Amazon' # replace legacy "AmazonECS" name
392
+ priority.uniq! # remove any other "Amazon" from the list
393
+ changed = true
394
+ end
395
+ if (deastore_index = priority.index('DeaStore_it'))
396
+ priority[deastore_index] = 'DeaStore'
397
+ changed = true
398
+ end
399
+ if (worldcat_index = priority.index('Worldcat'))
400
+ priority[worldcat_index] = 'WorldCat'
401
+ changed = true
402
+ end
403
+ if (adlibris_index = priority.index('Adlibris'))
404
+ priority[adlibris_index] = 'AdLibris'
405
+ changed = true
406
+ end
407
+ @prefs.providers_priority = priority if changed
408
+ end
409
+ end
410
+ end
411
+ end