alexandria-book-collection-manager 0.7.3 → 0.7.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (192) hide show
  1. checksums.yaml +4 -4
  2. data/.github/dependabot.yml +9 -0
  3. data/.github/workflows/ruby.yml +77 -0
  4. data/.gitignore +4 -1
  5. data/.rubocop.yml +86 -36
  6. data/.rubocop_todo.yml +58 -161
  7. data/.simplecov +5 -2
  8. data/CHANGELOG.md +56 -2
  9. data/Gemfile +4 -3
  10. data/INSTALL.md +23 -11
  11. data/README.md +52 -41
  12. data/Rakefile +78 -75
  13. data/alexandria-book-collection-manager.gemspec +50 -44
  14. data/bin/alexandria +12 -22
  15. data/doc/FAQ +1 -2
  16. data/doc/dependency_decisions.yml +27 -8
  17. data/lib/alexandria.rb +25 -23
  18. data/lib/alexandria/about.rb +50 -50
  19. data/lib/alexandria/book_providers.rb +86 -91
  20. data/lib/alexandria/book_providers/adlibris.rb +37 -74
  21. data/lib/alexandria/book_providers/amazon_aws.rb +94 -100
  22. data/lib/alexandria/book_providers/amazon_ecs_util.rb +289 -324
  23. data/lib/alexandria/book_providers/barnes_and_noble.rb +42 -42
  24. data/lib/alexandria/book_providers/douban.rb +25 -41
  25. data/lib/alexandria/book_providers/proxis.rb +34 -29
  26. data/lib/alexandria/book_providers/pseudomarc.rb +77 -85
  27. data/lib/alexandria/book_providers/siciliano.rb +60 -64
  28. data/lib/alexandria/book_providers/thalia_provider.rb +161 -0
  29. data/lib/alexandria/book_providers/web.rb +5 -5
  30. data/lib/alexandria/book_providers/worldcat.rb +66 -95
  31. data/lib/alexandria/book_providers/z3950.rb +153 -169
  32. data/lib/alexandria/config.rb +1 -1
  33. data/lib/alexandria/console.rb +3 -3
  34. data/lib/alexandria/default_preferences.rb +37 -0
  35. data/lib/alexandria/execution_queue.rb +13 -12
  36. data/lib/alexandria/export_format.rb +8 -8
  37. data/lib/alexandria/export_library.rb +128 -127
  38. data/lib/alexandria/import_library.rb +102 -126
  39. data/lib/alexandria/import_library_csv.rb +41 -41
  40. data/lib/alexandria/library_collection.rb +6 -5
  41. data/lib/alexandria/library_sort_order.rb +4 -2
  42. data/lib/alexandria/library_store.rb +39 -28
  43. data/lib/alexandria/logging.rb +10 -14
  44. data/lib/alexandria/models/book.rb +5 -4
  45. data/lib/alexandria/models/library.rb +63 -53
  46. data/lib/alexandria/net.rb +5 -6
  47. data/lib/alexandria/preferences.rb +66 -63
  48. data/lib/alexandria/scanners.rb +2 -2
  49. data/lib/alexandria/scanners/{cuecat.rb → cue_cat.rb} +17 -17
  50. data/lib/alexandria/scanners/keyboard.rb +8 -8
  51. data/lib/alexandria/smart_library.rb +110 -112
  52. data/lib/alexandria/ui.rb +15 -15
  53. data/lib/alexandria/ui/{dialogs/about_dialog.rb → about_dialog.rb} +2 -2
  54. data/lib/alexandria/ui/{dialogs/acquire_dialog.rb → acquire_dialog.rb} +108 -109
  55. data/lib/alexandria/ui/alert_dialog.rb +66 -0
  56. data/lib/alexandria/ui/{dialogs/bad_isbns_dialog.rb → bad_isbns_dialog.rb} +13 -9
  57. data/lib/alexandria/ui/{dialogs/barcode_animation.rb → barcode_animation.rb} +16 -15
  58. data/lib/alexandria/ui/{dialogs/book_properties_dialog.rb → book_properties_dialog.rb} +25 -38
  59. data/lib/alexandria/ui/{dialogs/book_properties_dialog_base.rb → book_properties_dialog_base.rb} +64 -157
  60. data/lib/alexandria/ui/builder_base.rb +1 -1
  61. data/lib/alexandria/ui/calendar_popup.rb +58 -0
  62. data/lib/alexandria/ui/callbacks.rb +187 -155
  63. data/lib/alexandria/ui/completion_models.rb +8 -22
  64. data/lib/alexandria/ui/confirm_erase_dialog.rb +33 -0
  65. data/lib/alexandria/ui/conflict_while_copying_dialog.rb +34 -0
  66. data/lib/alexandria/ui/dndable.rb +7 -7
  67. data/lib/alexandria/ui/error_dialog.rb +25 -0
  68. data/lib/alexandria/ui/{dialogs/export_dialog.rb → export_dialog.rb} +37 -58
  69. data/lib/alexandria/ui/icons.rb +38 -43
  70. data/lib/alexandria/ui/iconview.rb +12 -10
  71. data/lib/alexandria/ui/iconview_tooltips.rb +41 -54
  72. data/lib/alexandria/ui/import_dialog.rb +157 -0
  73. data/lib/alexandria/ui/init.rb +30 -41
  74. data/lib/alexandria/ui/{dialogs/keep_bad_isbn_dialog.rb → keep_bad_isbn_dialog.rb} +9 -6
  75. data/lib/alexandria/ui/libraries_combo.rb +15 -14
  76. data/lib/alexandria/ui/listview.rb +69 -67
  77. data/lib/alexandria/ui/main_app.rb +24 -26
  78. data/lib/alexandria/ui/misc_dialogs.rb +10 -0
  79. data/lib/alexandria/ui/multi_drag_treeview.rb +8 -9
  80. data/lib/alexandria/ui/{dialogs/new_book_dialog.rb → new_book_dialog.rb} +113 -114
  81. data/lib/alexandria/ui/{dialogs/new_book_dialog_manual.rb → new_book_dialog_manual.rb} +22 -19
  82. data/lib/alexandria/ui/new_provider_dialog.rb +100 -0
  83. data/lib/alexandria/ui/new_smart_library_dialog.rb +74 -0
  84. data/lib/alexandria/ui/preferences_dialog.rb +313 -0
  85. data/lib/alexandria/ui/provider_preferences_base_dialog.rb +95 -0
  86. data/lib/alexandria/ui/provider_preferences_dialog.rb +35 -0
  87. data/lib/alexandria/ui/{dialogs/misc_dialogs.rb → really_delete_dialog.rb} +7 -28
  88. data/lib/alexandria/ui/{sidepane.rb → sidepane_manager.rb} +53 -48
  89. data/lib/alexandria/ui/skip_entry_dialog.rb +33 -0
  90. data/lib/alexandria/ui/smart_library_properties_dialog.rb +60 -0
  91. data/lib/alexandria/ui/smart_library_properties_dialog_base.rb +242 -0
  92. data/lib/alexandria/ui/smart_library_rule_box.rb +119 -0
  93. data/lib/alexandria/ui/sound.rb +11 -13
  94. data/lib/alexandria/ui/ui_manager.rb +216 -200
  95. data/lib/alexandria/version.rb +4 -19
  96. data/lib/alexandria/web_themes.rb +21 -21
  97. data/po/Makefile +2 -2
  98. data/po/cs.po +992 -875
  99. data/po/cy.po +961 -874
  100. data/po/de.po +990 -865
  101. data/po/el.po +989 -865
  102. data/po/es.po +985 -861
  103. data/po/fr.po +987 -867
  104. data/po/ga.po +908 -820
  105. data/po/gl.po +980 -860
  106. data/po/it.po +986 -864
  107. data/po/ja.po +969 -849
  108. data/po/mk.po +984 -860
  109. data/po/nb.po +979 -859
  110. data/po/nl.po +983 -860
  111. data/po/pl.po +1018 -971
  112. data/po/pt.po +988 -857
  113. data/po/pt_BR.po +983 -863
  114. data/po/ru.po +994 -871
  115. data/po/sk.po +989 -867
  116. data/po/sv.po +976 -856
  117. data/po/uk.po +972 -858
  118. data/po/zh_TW.po +974 -854
  119. data/schemas/alexandria.schemas +24 -2
  120. data/share/alexandria/glade/acquire_dialog__builder.glade +1 -1
  121. data/share/alexandria/glade/book_properties_dialog__builder.glade +1 -1
  122. data/share/alexandria/glade/main_app__builder.glade +6 -21
  123. data/share/alexandria/glade/new_book_dialog__builder.glade +1 -1
  124. data/share/alexandria/glade/preferences_dialog__builder.glade +1 -1
  125. data/share/gnome/help/alexandria/C/introduction.xml +0 -4
  126. data/share/gnome/help/alexandria/C/searching.xml +1 -1
  127. data/share/gnome/help/alexandria/C/smart-libraries.xml +2 -2
  128. data/share/gnome/help/alexandria/C/working-with-libraries.xml +1 -1
  129. data/share/gnome/help/alexandria/fr/alexandria.xml +1 -1
  130. data/share/gnome/help/alexandria/ja/introduction.xml +0 -4
  131. data/share/gnome/help/alexandria/ja/smart-libraries.xml +1 -1
  132. data/spec/alexandria/book_providers/thalia_provider_spec.rb +119 -0
  133. data/spec/alexandria/book_providers/world_cat_provider_spec.rb +160 -0
  134. data/spec/alexandria/book_providers_spec.rb +62 -156
  135. data/spec/alexandria/book_spec.rb +12 -10
  136. data/spec/alexandria/console_spec.rb +6 -11
  137. data/spec/alexandria/export_library_spec.rb +47 -58
  138. data/spec/alexandria/library_spec.rb +121 -109
  139. data/spec/alexandria/library_store_spec.rb +8 -8
  140. data/spec/alexandria/preferences_spec.rb +44 -17
  141. data/spec/alexandria/scanners/cue_cat_spec.rb +52 -0
  142. data/spec/alexandria/smart_library_spec.rb +15 -15
  143. data/spec/alexandria/ui/about_dialog_spec.rb +14 -0
  144. data/spec/alexandria/ui/acquire_dialog_spec.rb +14 -0
  145. data/spec/alexandria/ui/alert_dialog_spec.rb +16 -0
  146. data/spec/alexandria/ui/bad_isbns_dialog_spec.rb +14 -0
  147. data/spec/alexandria/ui/book_properties_dialog_spec.rb +59 -0
  148. data/spec/alexandria/ui/confirm_erase_dialog_spec.rb +14 -0
  149. data/spec/alexandria/ui/conflict_while_copying_dialog_spec.rb +16 -0
  150. data/spec/alexandria/ui/error_dialog_spec.rb +14 -0
  151. data/spec/alexandria/ui/export_dialog_spec.rb +36 -0
  152. data/spec/alexandria/ui/icons_spec.rb +26 -0
  153. data/spec/alexandria/ui/iconview_spec.rb +7 -21
  154. data/spec/alexandria/ui/import_dialog_spec.rb +46 -0
  155. data/spec/alexandria/ui/keep_bad_isbn_dialog_spec.rb +17 -0
  156. data/spec/alexandria/ui/main_app_spec.rb +7 -34
  157. data/spec/alexandria/ui/new_book_dialog_manual_spec.rb +51 -0
  158. data/spec/alexandria/ui/{dialogs/new_book_dialog_spec.rb → new_book_dialog_spec.rb} +4 -4
  159. data/spec/alexandria/ui/new_provider_dialog_spec.rb +30 -0
  160. data/spec/alexandria/ui/new_smart_library_dialog_spec.rb +39 -0
  161. data/spec/alexandria/ui/preferences_dialog_spec.rb +14 -0
  162. data/spec/alexandria/ui/provider_preferences_dialog_spec.rb +34 -0
  163. data/spec/alexandria/ui/really_delete_dialog_spec.rb +16 -0
  164. data/spec/alexandria/ui/sidepane_manager_spec.rb +15 -0
  165. data/spec/alexandria/ui/skip_entry_dialog_spec.rb +14 -0
  166. data/spec/alexandria/ui/smart_library_properties_dialog_spec.rb +49 -0
  167. data/spec/alexandria/ui/sound_spec.rb +2 -2
  168. data/spec/alexandria/ui/ui_manager_spec.rb +44 -20
  169. data/spec/end_to_end/basic_run_spec.rb +21 -38
  170. data/spec/fixtures/cover.jpg +0 -0
  171. data/spec/spec_helper.rb +54 -10
  172. data/tasks/setup.rb +2 -2
  173. data/tasks/spec.rake +11 -11
  174. data/util/rake/fileinstall.rb +38 -35
  175. data/util/rake/gettextgenerate.rb +7 -7
  176. data/util/rake/omfgenerate.rb +7 -7
  177. metadata +158 -45
  178. data/dogtail/basic_run_test.py +0 -9
  179. data/lib/alexandria/book_providers/renaud.rb +0 -155
  180. data/lib/alexandria/book_providers/thalia.rb +0 -198
  181. data/lib/alexandria/ui/dialogs/alert_dialog.rb +0 -63
  182. data/lib/alexandria/ui/dialogs/import_dialog.rb +0 -176
  183. data/lib/alexandria/ui/dialogs/new_smart_library_dialog.rb +0 -62
  184. data/lib/alexandria/ui/dialogs/preferences_dialog.rb +0 -563
  185. data/lib/alexandria/ui/dialogs/smart_library_properties_dialog.rb +0 -61
  186. data/lib/alexandria/ui/dialogs/smart_library_properties_dialog_base.rb +0 -423
  187. data/spec/alexandria/scanners/cuecat_spec.rb +0 -67
  188. data/spec/alexandria/ui/dialogs_spec.rb +0 -162
  189. data/spec/alexandria/ui/sidepane_spec.rb +0 -29
  190. data/spec/alexandria/ui/ui_utilities_spec.rb +0 -62
  191. data/spec/alexandria/utilities_spec.rb +0 -52
  192. data/tasks/dogtail.rake +0 -6
@@ -24,7 +24,7 @@ module Alexandria
24
24
  class WWWAgent
25
25
  def initialize
26
26
  user_agent = "Ruby #{RUBY_VERSION} #{Alexandria::TITLE}/#{Alexandria::VERSION}"
27
- @extra_request_headers = { 'User-Agent' => user_agent }
27
+ @extra_request_headers = { "User-Agent" => user_agent }
28
28
  end
29
29
 
30
30
  def self.transport
@@ -38,18 +38,17 @@ module Alexandria
38
38
  @extra_request_headers.each_pair do |header_name, value|
39
39
  req.add_field(header_name, value)
40
40
  end
41
- res = WWWAgent.transport.start(uri.host, uri.port) { |http|
41
+ WWWAgent.transport.start(uri.host, uri.port) do |http|
42
42
  http.request(req)
43
- }
44
- res
43
+ end
45
44
  end
46
45
 
47
46
  def language=(lang)
48
- @extra_request_headers['Accept-Language'] = lang.to_s
47
+ @extra_request_headers["Accept-Language"] = lang.to_s
49
48
  end
50
49
 
51
50
  def user_agent=(agent_string)
52
- @extra_request_headers['User-Agent'] = agent_string
51
+ @extra_request_headers["User-Agent"] = agent_string
53
52
  end
54
53
  end
55
54
  end
@@ -4,21 +4,21 @@
4
4
  #
5
5
  # See the file README.md for authorship and licensing information.
6
6
 
7
- require 'singleton'
8
- require 'set'
9
- require 'alexandria/default_preferences'
7
+ require "singleton"
8
+ require "set"
9
+ require "alexandria/default_preferences"
10
10
 
11
11
  module Alexandria
12
12
  class Preferences
13
13
  include Singleton
14
14
  include Logging
15
15
 
16
- APP_DIR = '/apps/alexandria'
17
- HTTP_PROXY_DIR = '/system/http_proxy'
18
- HTTP_PROXY_MODE = '/system/proxy/mode'
19
- URL_HANDLERS_DIR = '/desktop/gnome/url-handlers'
16
+ APP_DIR = "/apps/alexandria"
17
+ HTTP_PROXY_DIR = "/system/http_proxy"
18
+ HTTP_PROXY_MODE = "/system/proxy/mode"
19
+ URL_HANDLERS_DIR = "/desktop/gnome/url-handlers"
20
20
 
21
- GCONFTOOL = 'gconftool-2'
21
+ GCONFTOOL = "gconftool-2"
22
22
 
23
23
  def initialize
24
24
  @alexandria_settings = {}
@@ -46,38 +46,35 @@ module Alexandria
46
46
  end
47
47
 
48
48
  def save!
49
- log.debug { 'preferences save!' }
49
+ log.debug { "preferences save!" }
50
50
  @changed_settings.each do |variable_name|
51
- log.debug { "saving preference #{variable_name} / #{@alexandria_settings[variable_name].class}" }
51
+ log.debug do
52
+ klass = @alexandria_settings[variable_name].class
53
+ "saving preference #{variable_name} / #{klass}"
54
+ end
52
55
  generic_save_setting(variable_name, @alexandria_settings[variable_name])
53
56
  end
54
57
  @changed_settings.clear
55
58
  end
56
59
 
57
- def method_missing(id, *args)
58
- method = id.id2name
59
- if (match = /(.*)=$/.match(method))
60
- if args.length != 1
61
- raise "Set method #{method} should be called with " \
62
- "only one argument (was called with #{args.length})"
63
- end
64
- variable_name = match[1]
65
- new_value = args.first
66
- generic_setter(variable_name, new_value)
67
- else
68
- unless args.empty?
69
- raise "Get method #{method} should be called " \
70
- "without argument (was called with #{args.length})"
71
- end
72
- generic_getter(method)
73
- end
74
- end
75
-
76
60
  def remove_preference(variable_name)
77
61
  @alexandria_settings.delete(variable_name)
78
62
  @changed_settings << variable_name
79
63
  end
80
64
 
65
+ DEFAULT_VALUES.each_key do |var|
66
+ define_method(var) { generic_getter var }
67
+ define_method("#{var}=") { |val| generic_setter var, val }
68
+ end
69
+
70
+ def get_variable(variable_name)
71
+ generic_getter(variable_name.to_s)
72
+ end
73
+
74
+ def set_variable(variable_name, value)
75
+ generic_setter(variable_name.to_s, value)
76
+ end
77
+
81
78
  private
82
79
 
83
80
  ##
@@ -107,7 +104,7 @@ module Alexandria
107
104
  end
108
105
 
109
106
  def generic_save_setting(variable_name, new_value)
110
- var_path = APP_DIR + '/' + variable_name
107
+ var_path = APP_DIR + "/" + variable_name
111
108
  if new_value.is_a?(Array)
112
109
  # when setting array, first remove nil elements (fixing #9007)
113
110
  new_value.compact!
@@ -125,7 +122,9 @@ module Alexandria
125
122
  end
126
123
  rescue StandardError => ex
127
124
  log.debug { new_value.inspect }
128
- log.error { "Could not set GConf setting #{variable_name} to value: #{new_value.inspect}" }
125
+ log.error do
126
+ "Could not set GConf setting #{variable_name} to value: #{new_value.inspect}"
127
+ end
129
128
  log << ex.message
130
129
  log << ex
131
130
  end
@@ -135,14 +134,15 @@ module Alexandria
135
134
  ##
136
135
 
137
136
  def get_gconf_type(value)
138
- if value.is_a?(String)
139
- 'string'
140
- elsif value.is_a?(Integer)
141
- 'int'
142
- elsif value.is_a?(TrueClass) || value.is_a?(FalseClass)
143
- 'bool'
137
+ case value
138
+ when String
139
+ "string"
140
+ when Integer
141
+ "int"
142
+ when TrueClass, FalseClass
143
+ "bool"
144
144
  else
145
- 'string'
145
+ "string"
146
146
  end
147
147
  end
148
148
 
@@ -150,7 +150,7 @@ module Alexandria
150
150
  # NOTE we must check between list and pair...
151
151
 
152
152
  list_type = get_gconf_type(new_list.first)
153
- if list_type == 'int' && new_list.size == 2
153
+ if list_type == "int" && new_list.size == 2
154
154
  # treat this as a pair of int
155
155
  a = new_list[0]
156
156
  b = new_list[1]
@@ -163,29 +163,29 @@ module Alexandria
163
163
  end
164
164
 
165
165
  def make_list_string(list)
166
- list.map! { |x| x.gsub(/\"/, '\\"') } if get_gconf_type(list.first) == 'string'
167
- contents = list.join(',')
168
- '[' + contents + ']'
166
+ list.map! { |x| x.gsub(/"/, '\\"') } if get_gconf_type(list.first) == "string"
167
+ contents = list.join(",")
168
+ "[" + contents + "]"
169
169
  end
170
170
 
171
171
  def exec_gconf_set(var_path, new_value)
172
- if /cols_width/ =~ var_path
173
- puts new_value
172
+ if /cols_width/.match?(var_path)
173
+ log.debug { new_value }
174
174
 
175
175
  # new_value = {}
176
176
  end
177
177
  type = get_gconf_type(new_value)
178
178
  value_str = new_value
179
179
  if new_value.is_a? String
180
- new_value.gsub!(/\"/, '\\"')
180
+ new_value = new_value.gsub(/"/, '\\"')
181
181
  value_str = "\"#{new_value}\""
182
182
  end
183
- puts value_str if /cols_width/ =~ var_path
183
+ log.debug { value_str } if /cols_width/.match?(var_path)
184
184
  `gconftool-2 --type #{type} --set #{var_path} #{value_str}`
185
185
  end
186
186
 
187
187
  def exec_gconf_unset(variable_name)
188
- `#{GCONFTOOL} --unset #{APP_DIR + '/' + variable_name}`
188
+ `#{GCONFTOOL} --unset #{APP_DIR + "/" + variable_name}`
189
189
  end
190
190
 
191
191
  ##
@@ -205,14 +205,14 @@ module Alexandria
205
205
  def load_http_proxy_settings
206
206
  http_proxy_vars = `#{GCONFTOOL} --recursive-list #{HTTP_PROXY_DIR}`
207
207
  http_proxy = gconftool_values_to_hash(http_proxy_vars)
208
- if http_proxy['use_http_proxy']
208
+ if http_proxy["use_http_proxy"]
209
209
  proxy_mode = `#{GCONFTOOL} --get #{HTTP_PROXY_MODE}`.chomp
210
- if proxy_mode == 'manual'
210
+ if proxy_mode == "manual"
211
211
  @use_http_proxy = true
212
- @proxy_host = http_proxy['host']
213
- @proxy_port = http_proxy['port']
214
- @proxy_user = http_proxy['authentication_user']
215
- @proxy_password = http_proxy['authentication_n_password']
212
+ @proxy_host = http_proxy["host"]
213
+ @proxy_port = http_proxy["port"]
214
+ @proxy_user = http_proxy["authentication_user"]
215
+ @proxy_password = http_proxy["authentication_n_password"]
216
216
  end
217
217
  end
218
218
  @http_proxy_loaded = true
@@ -226,7 +226,9 @@ module Alexandria
226
226
  hash = {}
227
227
  vals = all_vals.split(/$/)
228
228
  vals.each do |val|
229
- hash[Regexp.last_match[1]] = discriminate(Regexp.last_match[2]) if /([a-z_]+) = (.*)/ =~ val
229
+ if /([a-z_]+) = (.*)/ =~ val
230
+ hash[Regexp.last_match[1]] = discriminate(Regexp.last_match[2])
231
+ end
230
232
  end
231
233
  hash
232
234
  end
@@ -235,20 +237,21 @@ module Alexandria
235
237
  # gconftool. This is not fool-proof, but it *does* work for the
236
238
  # range of values used by Alexandria.
237
239
  def discriminate(value)
238
- if value == 'true' # bool
240
+ case value
241
+ when "true" # bool
239
242
  true
240
- elsif value == 'false' # bool
243
+ when "false" # bool
241
244
  false
242
- elsif value =~ /^[0-9]+$/ # int
245
+ when /^[0-9]+$/ # int
243
246
  value.to_i
244
- elsif value =~ /^\[(.*)\]$/ # list (assume of type String)
245
- Regexp.last_match[1].split(',')
246
- elsif value =~ /^\((.*)\)$/ # pair (assume of type int)
247
+ when /^\[(.*)\]$/ # list (assume of type String)
248
+ Regexp.last_match[1].split(",")
249
+ when /^\((.*)\)$/ # pair (assume of type int)
247
250
  begin
248
- pair = Regexp.last_match[1].split(',')
249
- return [discriminate(pair.first), discriminate(pair.last)]
251
+ pair = Regexp.last_match[1].split(",")
252
+ [discriminate(pair.first), discriminate(pair.last)]
250
253
  rescue StandardError
251
- return [0, 0]
254
+ [0, 0]
252
255
  end
253
256
  else
254
257
  value # string
@@ -40,8 +40,8 @@ module Alexandria
40
40
  registry.find { |scanner| scanner.name == name }
41
41
  end
42
42
 
43
- def self.each_scanner
44
- registry.each { |scanner| yield scanner }
43
+ def self.each_scanner(&block)
44
+ registry.each(&block)
45
45
  end
46
46
 
47
47
  private_class_method :registry
@@ -18,27 +18,27 @@
18
18
  # write to the Free Software Foundation, Inc., 51 Franklin Street,
19
19
  # Fifth Floor, Boston, MA 02110-1301 USA.
20
20
 
21
- require 'alexandria/scanners'
21
+ require "alexandria/scanners"
22
22
 
23
23
  module Alexandria
24
24
  module Scanners
25
25
  class CueCat
26
- include Alexandria::Logging
26
+ include Logging
27
27
 
28
28
  def name
29
- 'CueCat'
29
+ "CueCat"
30
30
  end
31
31
 
32
32
  def display_name
33
- 'CueCat'
33
+ "CueCat"
34
34
  end
35
35
 
36
36
  # Checks if data looks like cuecat input
37
37
  def match?(data)
38
38
  data = data.chomp
39
- return false if data[-1] != '.'
39
+ return false if data[-1] != "."
40
40
 
41
- fields = data.split('.')
41
+ fields = data.split(".")
42
42
  return false if fields.size != 4
43
43
  return false if fields[2].size != 4
44
44
 
@@ -50,13 +50,13 @@ module Alexandria
50
50
  # domain perl implementation.
51
51
  def decode(data)
52
52
  data = data.chomp
53
- fields = data.split('.')
53
+ fields = data.split(".")
54
54
  fields.shift # First part is gibberish
55
55
  fields.shift # Second part is cuecat serial number
56
56
  type, code = fields.map { |field| decode_field(field) }
57
57
 
58
- if type == 'IB5'
59
- type = 'IBN'
58
+ if type == "IB5"
59
+ type = "IBN"
60
60
  code = code[0, 13]
61
61
  end
62
62
 
@@ -64,33 +64,33 @@ module Alexandria
64
64
  if Library.valid_upc? code
65
65
  isbn13 = Library.canonicalise_ean(code)
66
66
  code = isbn13
67
- type = 'IBN'
67
+ type = "IBN"
68
68
  end
69
69
  rescue StandardError
70
70
  log.debug { "Cannot translate UPC (#{type}) code #{code} to ISBN" }
71
71
  end
72
72
 
73
- return code if type == 'IBN'
73
+ return code if type == "IBN"
74
74
 
75
- raise "Don't know how to handle type #{type} (barcode: #{code})"
75
+ raise format(_("Don't know how to handle type %<type>s (barcode: %<code>s)"),
76
+ type: type, code: code)
76
77
  end
77
78
 
78
79
  private
79
80
 
80
81
  def decode_field(encoded)
81
- seq = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+-'
82
+ seq = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+-"
82
83
 
83
84
  chars = encoded.split(//)
84
85
  values = chars.map { |c| seq.index(c) }
85
86
 
86
87
  padding = pad(values)
87
88
  result = calc(values)
88
- result = result[0, result.length - padding]
89
- result
89
+ result[0, result.length - padding]
90
90
  end
91
91
 
92
92
  def calc(values)
93
- result = ''
93
+ result = ""
94
94
  until values.empty?
95
95
  num = ((values[0] << 6 | values[1]) << 6 | values[2]) << 6 | values[3]
96
96
  result += ((num >> 16) ^ 67).chr
@@ -106,7 +106,7 @@ module Alexandria
106
106
  length = array.length % 4
107
107
 
108
108
  if length.nonzero?
109
- raise 'Error parsing CueCat input' if length == 1
109
+ raise _("Error parsing CueCat input") if length == 1
110
110
 
111
111
  length = 4 - length
112
112
  length.times { array.push(0) }
@@ -18,7 +18,7 @@
18
18
  # write to the Free Software Foundation, Inc., 51 Franklin Street,
19
19
  # Fifth Floor, Boston, MA 02110-1301 USA.
20
20
 
21
- require 'alexandria/scanners'
21
+ require "alexandria/scanners"
22
22
 
23
23
  module Alexandria
24
24
  module Scanners
@@ -27,28 +27,28 @@ module Alexandria
27
27
  # like this.)
28
28
  class KeyboardWedge
29
29
  def name
30
- 'KeyboardWedge'
30
+ "KeyboardWedge"
31
31
  end
32
32
 
33
33
  def display_name
34
- 'Keyboard Wedge'
34
+ "Keyboard Wedge"
35
35
  end
36
36
 
37
37
  # Checks if data looks like a completed scan
38
38
  def match?(data)
39
- data.gsub!(/\s/, '')
39
+ data.gsub!(/\s/, "")
40
40
  (data =~ /[0-9]{12,18}/) || (data =~ /[0-9]{9}[0-9Xx]/)
41
41
  end
42
42
 
43
43
  # Gets the essential 13-digits from an ISBN barcode (EAN-13)
44
44
  def decode(data)
45
- data.gsub!(/\s/, '')
45
+ data.gsub!(/\s/, "")
46
46
  if data.length == 10
47
- return data
47
+ data
48
48
  elsif data.length >= 13
49
- return data[0, 13]
49
+ data[0, 13]
50
50
  else
51
- raise "Unknown scan data #{data}"
51
+ raise format(_("Unknown scan data %s<data>"), data: data)
52
52
  end
53
53
  end
54
54
  end
@@ -5,27 +5,27 @@
5
5
  # See the file README.md for authorship and licensing information.
6
6
 
7
7
  # require 'date'
8
- require 'time'
8
+ require "time"
9
9
 
10
10
  module Alexandria
11
11
  class SmartLibrary < Array
12
12
  include Logging
13
13
  include GetText
14
14
  extend GetText
15
- bindtextdomain(Alexandria::TEXTDOMAIN, charset: 'UTF-8')
15
+ bindtextdomain(Alexandria::TEXTDOMAIN, charset: "UTF-8")
16
16
 
17
17
  ALL_RULES = 1
18
18
  ANY_RULE = 2
19
- attr_reader :name
19
+ attr_reader :name, :n_rated
20
20
  attr_accessor :rules, :predicate_operator_rule, :deleted_books
21
21
 
22
- EXT = '.yaml'
22
+ EXT = ".yaml"
23
23
 
24
24
  def initialize(name, rules, predicate_operator_rule, store = nil)
25
25
  super()
26
26
  raise if name.nil? || rules.nil? || predicate_operator_rule.nil?
27
27
 
28
- @name = name.dup.force_encoding('UTF-8')
28
+ @name = name.dup.force_encoding("UTF-8")
29
29
  @rules = rules
30
30
  @predicate_operator_rule = predicate_operator_rule
31
31
  @store = store
@@ -46,25 +46,25 @@ module Alexandria
46
46
  rule = Rule.new(operands.find { |x| x.book_selector == :rating },
47
47
  Rule::Operators::IS,
48
48
  Book::MAX_RATING_STARS.to_s)
49
- a << new(_('Favorite'), [rule], ALL_RULES, store)
49
+ a << new(_("Favorite"), [rule], ALL_RULES, store)
50
50
 
51
51
  # Loaned books.
52
52
  rule = Rule.new(operands.find { |x| x.book_selector == :loaned },
53
53
  Rule::Operators::IS_TRUE,
54
54
  nil)
55
- a << new(_('Loaned'), [rule], ALL_RULES, store)
55
+ a << new(_("Loaned"), [rule], ALL_RULES, store)
56
56
 
57
57
  # Redd books.
58
58
  rule = Rule.new(operands.find { |x| x.book_selector == :redd },
59
59
  Rule::Operators::IS_TRUE,
60
60
  nil)
61
- a << new(_('Read'), [rule], ALL_RULES, store)
61
+ a << new(_("Read"), [rule], ALL_RULES, store)
62
62
 
63
63
  # Own books.
64
64
  rule = Rule.new(operands.find { |x| x.book_selector == :own },
65
65
  Rule::Operators::IS_TRUE,
66
66
  nil)
67
- a << new(_('Owned'), [rule], ALL_RULES, store)
67
+ a << new(_("Owned"), [rule], ALL_RULES, store)
68
68
 
69
69
  # Want books.
70
70
  rule = Rule.new(operands.find { |x| x.book_selector == :want },
@@ -73,7 +73,7 @@ module Alexandria
73
73
  rule2 = Rule.new(operands.find { |x| x.book_selector == :own },
74
74
  Rule::Operators::IS_NOT_TRUE,
75
75
  nil)
76
- a << new(_('Wishlist'), [rule, rule2], ALL_RULES, store)
76
+ a << new(_("Wishlist"), [rule, rule2], ALL_RULES, store)
77
77
 
78
78
  a
79
79
  end
@@ -94,22 +94,23 @@ module Alexandria
94
94
  end
95
95
 
96
96
  def name=(new_name)
97
- if @name != new_name
98
- old_yaml = yaml
99
- @name = new_name
100
- FileUtils.mv(old_yaml, yaml)
101
- save
102
- end
97
+ return unless @name != new_name
98
+
99
+ old_yaml = yaml
100
+ @name = new_name
101
+ FileUtils.mv(old_yaml, yaml)
102
+ save
103
103
  end
104
104
 
105
105
  def update(*params)
106
- if params.first.is_a?(LibraryCollection)
106
+ case params.first
107
+ when LibraryCollection
107
108
  libraries, _, library = params
108
109
  unless library.is_a?(self.class)
109
110
  self.libraries = libraries.all_libraries
110
111
  refilter
111
112
  end
112
- elsif params.first.is_a?(Library)
113
+ when Library
113
114
  refilter
114
115
  end
115
116
  end
@@ -148,7 +149,7 @@ module Alexandria
148
149
  @cache[book].save(book)
149
150
  else
150
151
  FileUtils.mkdir_p(base_dir) unless File.exist? base_dir
151
- File.open(yaml, 'w') { |io| io.puts to_hash.to_yaml }
152
+ File.open(yaml, "w") { |io| io.puts to_hash.to_yaml }
152
153
  end
153
154
  end
154
155
 
@@ -172,14 +173,12 @@ module Alexandria
172
173
  end
173
174
  end
174
175
 
175
- attr_reader :n_rated
176
-
177
176
  def n_unrated
178
177
  length - n_rated
179
178
  end
180
179
 
181
- def ==(object)
182
- object.is_a?(self.class) && object.name == name
180
+ def ==(other)
181
+ other.is_a?(self.class) && other.name == name
183
182
  end
184
183
 
185
184
  @@deleted_libraries = []
@@ -190,15 +189,16 @@ module Alexandria
190
189
 
191
190
  def self.really_delete_deleted_libraries
192
191
  @@deleted_libraries.each do |library|
193
- puts "Deleting smart library file (#{yaml})" if $DEBUG
192
+ log.debug { "Deleting smart library file (#{yaml})" }
194
193
  FileUtils.rm_rf(library.yaml)
195
194
  end
196
195
  end
197
196
 
198
197
  def delete
199
198
  if @@deleted_libraries.include?(self)
200
- puts 'Already deleted a SmartLibrary with this name'
201
- puts '(this might mess up undeletes...)'
199
+ log.info do
200
+ "Already deleted a SmartLibrary with this name (this might mess up undeletes)"
201
+ end
202
202
  FileUtils.rm_rf(yaml)
203
203
  # so we just delete the old smart library, and
204
204
  # 'pending' delete the new one of the same name...
@@ -233,7 +233,7 @@ module Alexandria
233
233
  class Rule
234
234
  include GetText
235
235
  extend GetText
236
- bindtextdomain(Alexandria::TEXTDOMAIN, charset: 'UTF-8')
236
+ bindtextdomain(Alexandria::TEXTDOMAIN, charset: "UTF-8")
237
237
 
238
238
  attr_accessor :operand, :operation, :value
239
239
 
@@ -265,8 +265,8 @@ module Alexandria
265
265
 
266
266
  Operand = Struct.new(:name, :klass)
267
267
  class Operand
268
- def <=>(x)
269
- name <=> x.name
268
+ def <=>(other)
269
+ name <=> other.name
270
270
  end
271
271
  end
272
272
 
@@ -281,123 +281,125 @@ module Alexandria
281
281
 
282
282
  Operator = Struct.new(:sym, :name, :proc)
283
283
  class Operator
284
- def <=>(x)
285
- name <=> x.name
284
+ def <=>(other)
285
+ name <=> other.name
286
286
  end
287
287
  end
288
288
 
289
289
  module Operands
290
290
  include GetText
291
291
  extend GetText
292
- bindtextdomain(Alexandria::TEXTDOMAIN, charset: 'UTF-8')
292
+ bindtextdomain(Alexandria::TEXTDOMAIN, charset: "UTF-8")
293
293
 
294
294
  LEFT = [
295
- LeftOperand.new(:title, _('Title'), String),
296
- LeftOperand.new(:isbn, _('ISBN'), String),
297
- LeftOperand.new(:authors, _('Authors'), String),
298
- LeftOperand.new(:publisher, _('Publisher'), String),
299
- LeftOperand.new(:publishing_year, _('Publish Year'), Integer),
300
- LeftOperand.new(:edition, _('Binding'), String),
301
- LeftOperand.new(:rating, _('Rating'), Integer),
302
- LeftOperand.new(:notes, _('Notes'), String),
303
- LeftOperand.new(:tags, _('Tags'), Array),
304
- LeftOperand.new(:loaned, _('Loaning State'), TrueClass),
305
- LeftOperand.new(:loaned_since, _('Loaning Date'), Time),
306
- LeftOperand.new(:loaned_to, _('Loaning Person'), String),
307
- LeftOperand.new(:redd, _('Read'), TrueClass),
308
- LeftOperand.new(:redd_when, _('Date Read'), Time),
309
- LeftOperand.new(:own, _('Own'), TrueClass),
310
- LeftOperand.new(:want, _('Want'), TrueClass),
295
+ LeftOperand.new(:title, _("Title"), String),
296
+ LeftOperand.new(:isbn, _("ISBN"), String),
297
+ LeftOperand.new(:authors, _("Authors"), String),
298
+ LeftOperand.new(:publisher, _("Publisher"), String),
299
+ LeftOperand.new(:publishing_year, _("Publish Year"), Integer),
300
+ LeftOperand.new(:edition, _("Binding"), String),
301
+ LeftOperand.new(:rating, _("Rating"), Integer),
302
+ LeftOperand.new(:notes, _("Notes"), String),
303
+ LeftOperand.new(:tags, _("Tags"), Array),
304
+ LeftOperand.new(:loaned, _("Loaning State"), TrueClass),
305
+ LeftOperand.new(:loaned_since, _("Loaning Date"), Time),
306
+ LeftOperand.new(:loaned_to, _("Loaning Person"), String),
307
+ LeftOperand.new(:redd, _("Read"), TrueClass),
308
+ LeftOperand.new(:redd_when, _("Date Read"), Time),
309
+ LeftOperand.new(:own, _("Own"), TrueClass),
310
+ LeftOperand.new(:want, _("Want"), TrueClass),
311
311
  ].sort
312
312
 
313
313
  STRING = Operand.new(nil, String)
314
314
  STRING_ARRAY = Operand.new(nil, String)
315
315
  INTEGER = Operand.new(nil, Integer)
316
316
  TIME = Operand.new(nil, Time)
317
- DAYS = Operand.new(_('days'), Integer)
317
+ DAYS = Operand.new(_("days"), Integer)
318
318
  end
319
319
 
320
320
  module Operators
321
321
  include Logging
322
322
  include GetText
323
323
  extend GetText
324
- bindtextdomain(Alexandria::TEXTDOMAIN, charset: 'UTF-8')
324
+ bindtextdomain(Alexandria::TEXTDOMAIN, charset: "UTF-8")
325
325
 
326
326
  IS_TRUE = Operator.new(:is_true,
327
- _('is set'),
327
+ _("is set"),
328
328
  proc { |x| x })
329
329
  IS_NOT_TRUE = Operator.new(:is_not_true,
330
- _('is not set'),
330
+ _("is not set"),
331
331
  proc { |x| !x })
332
332
  IS = Operator.new(:is,
333
- _('is'),
333
+ _("is"),
334
334
  proc { |x, y| x == y })
335
335
  IS_NOT = Operator.new(:is_not,
336
- _('is not'),
336
+ _("is not"),
337
337
  proc { |x, y| x != y })
338
338
  CONTAINS = Operator.new(:contains,
339
- _('contains'),
339
+ _("contains"),
340
340
  proc { |x, y| x.include?(y) })
341
341
  DOES_NOT_CONTAIN = Operator.new(:does_not_contain,
342
- _('does not contain'),
342
+ _("does not contain"),
343
343
  proc { |x, y| !x.include?(y) })
344
344
  STARTS_WITH = Operator.new(:starts_with,
345
- _('starts with'),
345
+ _("starts with"),
346
346
  proc { |x, y| /^#{y}/.match(x) })
347
347
  ENDS_WITH = Operator.new(:ends_with,
348
- _('ends with'),
348
+ _("ends with"),
349
349
  proc { |x, y| /#{y}$/.match(x) })
350
350
  IS_GREATER_THAN = Operator.new(:is_greater_than,
351
- _('is greater than'),
351
+ _("is greater than"),
352
352
  proc { |x, y| x > y })
353
353
  IS_LESS_THAN = Operator.new(:is_less_than,
354
- _('is less than'),
354
+ _("is less than"),
355
355
  proc { |x, y| x < y })
356
356
  IS_AFTER = Operator.new(:is_after,
357
- _('is after'),
357
+ _("is after"),
358
358
  proc { |x, y| x.to_i > y.to_i && !x.nil? })
359
359
  IS_BEFORE = Operator.new(:is_before,
360
- _('is before'),
360
+ _("is before"),
361
361
  proc { |x, y| x.to_i < y.to_i && !x.nil? })
362
- IS_IN_LAST = Operator.new(:is_in_last_days,
363
- _('is in last'),
364
- proc { |x, y|
365
- begin
366
- if x.nil? || x.empty?
367
- false
368
- else
369
- log.debug { "Given Date: #{x.inspect} #{x.class}" }
370
- given_date = Time.parse(x)
371
- days = y.to_i * (24 * 60 * 60)
372
-
373
- Time.now - given_date <= days
374
- end
375
- rescue StandardError => ex
376
- trace = ex.backtrace.join("\n >")
377
- log.warn { "Date matching failed #{ex} #{trace}" }
378
- false
379
- end
380
- })
381
- IS_NOT_IN_LAST = Operator.new(:is_not_in_last_days,
382
- _('is not in last'),
383
- proc { |x, y|
384
- begin
385
- if x.nil? || x.empty?
386
- false
387
- else
388
- log.debug { "Given Date: #{x.inspect} #{x.class}" }
389
- given_date = Time.parse(x)
390
- days = y.to_i * (24 * 60 * 60)
391
-
392
- Time.now - given_date > days
393
- end
394
- rescue StandardError => ex
395
- trace = ex.backtrace.join("\n >")
396
- log.warn { "Date matching failed #{ex} #{trace}" }
397
- false
398
- end
399
- # Time.now - x > 3600*24*y
400
- })
362
+ IS_IN_LAST =
363
+ Operator.new(:is_in_last_days,
364
+ _("is in last"),
365
+ proc { |x, y|
366
+ begin
367
+ if x.nil? || x.empty?
368
+ false
369
+ else
370
+ log.debug { "Given Date: #{x.inspect} #{x.class}" }
371
+ given_date = Time.parse(x)
372
+ days = y.to_i * (24 * 60 * 60)
373
+
374
+ Time.now - given_date <= days
375
+ end
376
+ rescue StandardError => ex
377
+ trace = ex.backtrace.join("\n >")
378
+ log.warn { "Date matching failed #{ex} #{trace}" }
379
+ false
380
+ end
381
+ })
382
+ IS_NOT_IN_LAST =
383
+ Operator.new(:is_not_in_last_days,
384
+ _("is not in last"),
385
+ proc { |x, y|
386
+ begin
387
+ if x.nil? || x.empty?
388
+ false
389
+ else
390
+ log.debug { "Given Date: #{x.inspect} #{x.class}" }
391
+ given_date = Time.parse(x)
392
+ days = y.to_i * (24 * 60 * 60)
393
+
394
+ Time.now - given_date > days
395
+ end
396
+ rescue StandardError => ex
397
+ trace = ex.backtrace.join("\n >")
398
+ log.warn { "Date matching failed #{ex} #{trace}" }
399
+ false
400
+ end
401
+ # Time.now - x > 3600*24*y
402
+ })
401
403
 
402
404
  ALL = constants.map \
403
405
  { |x| module_eval(x.to_s) }.select \
@@ -441,15 +443,15 @@ module Alexandria
441
443
 
442
444
  def self.operations_for_operand(operand)
443
445
  case operand.klass.name
444
- when 'String'
446
+ when "String"
445
447
  STRING_OPERATORS.map { |x| [x, Operands::STRING] }
446
- when 'Array'
448
+ when "Array"
447
449
  STRING_ARRAY_OPERATORS.map { |x| [x, Operands::STRING] }
448
- when 'Integer'
450
+ when "Integer"
449
451
  INTEGER_OPERATORS.map { |x| [x, Operands::INTEGER] }
450
- when 'TrueClass'
452
+ when "TrueClass"
451
453
  BOOLEAN_OPERATORS.map { |x| [x, nil] }
452
- when 'Time'
454
+ when "Time"
453
455
  TIME_OPERATORS.map do |x|
454
456
  if (x == Operators::IS_IN_LAST) ||
455
457
  (x == Operators::IS_NOT_IN_LAST)
@@ -460,17 +462,13 @@ module Alexandria
460
462
  end
461
463
  end
462
464
  else
463
- raise "invalid operand klass #{operand.klass}"
465
+ raise format(_("invalid operand klass %<klass>s"), klass: operand.klass)
464
466
  end
465
467
  end
466
468
 
467
469
  def filter_proc
468
470
  proc do |book|
469
- begin
470
- left_value = book.send(@operand.book_selector)
471
- rescue StandardError => e
472
- puts e.message
473
- end
471
+ left_value = book.send(@operand.book_selector)
474
472
  right_value = @value
475
473
  if right_value.is_a?(String)
476
474
  left_value = left_value.to_s.downcase