C0 code coverage information
Generated on Tue Oct 16 11:40:47 -0400 2007 with rcov 0.8.0
Code reported as executed by Ruby looks like this...
and this: this line is also marked as covered.
Lines considered as run by rcov, but not reported by Ruby, look like this,
and this: these lines were inferred by rcov (using simple heuristics).
Finally, here's a line marked as not executed.
1 # Copyright (C) 2004-2006 Laurent Sansonetti
2 #
3 # Alexandria is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License as
5 # published by the Free Software Foundation; either version 2 of the
6 # License, or (at your option) any later version.
7 #
8 # Alexandria is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 # General Public License for more details.
12 #
13 # You should have received a copy of the GNU General Public
14 # License along with Alexandria; see the file COPYING. If not,
15 # write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
16 # Boston, MA 02111-1307, USA.
17
18 require 'singleton'
19
20 module Alexandria
21 class BookProviders < Array
22 include Singleton
23 include GetText
24 GetText.bindtextdomain(Alexandria::TEXTDOMAIN, nil, nil, "UTF-8")
25
26 SEARCH_BY_ISBN, SEARCH_BY_TITLE, SEARCH_BY_AUTHORS,
27 SEARCH_BY_KEYWORD = (0..3).to_a
28
29 class SearchError < StandardError; end
30 class NoResultsError < SearchError; end
31 class TooManyResultsError < SearchError; end
32 class InvalidSearchTypeError < SearchError; end
33
34 def self.search(criterion, type)
35 factory_n = 0
36 begin
37 factory = self.instance[factory_n]
38 puts factory.fullname + " lookup" if $DEBUG
39 results = factory.search(criterion, type)
40
41 if results.length == 0
42 raise NoResultsError
43 else
44 puts "found at " + factory.fullname
45 return results
46 end
47 rescue Exception => boom
48 if self.last == factory
49 puts "Error while searching #{criterion}"
50 raise case boom
51 when Timeout::Error
52 _("Couldn't reach the provider '%s': timeout " +
53 "expired.") % factory.name
54
55 when SocketError
56 _("Couldn't reach the provider '%s': socket " +
57 "error (%s).") % [factory.name, boom.message]
58
59 when NoResultsError
60 _("No results were found. Make sure your " +
61 "search criterion is spelled correctly, and " +
62 "try again.")
63
64 when TooManyResultsError
65 _("Too many results for that search.")
66
67 when InvalidSearchTypeError
68 _("Invalid search type.")
69
70 else
71 boom.message
72 end
73 else
74 factory_n += 1
75 retry
76 end
77 end
78 end
79
80 def self.isbn_search(criterion)
81 self.search(criterion, SEARCH_BY_ISBN)
82 end
83
84 class Preferences < Array
85 def initialize(provider)
86 @provider = provider
87 end
88
89 class Variable
90 attr_reader :provider_name, :name, :description,
91 :possible_values
92 attr_accessor :value
93
94 def initialize(provider, name, description, default_value,
95 possible_values=nil, mandatory=true)
96
97 @provider = provider
98 @name = name
99 @description = description
100 @value = default_value
101 @possible_values = possible_values
102 @mandatory = mandatory
103 end
104
105 def default_value=(new_value)
106 self.value = new_value
107 end
108
109 def new_value=(new_value)
110 message = @provider.variable_name(self) + '='
111 Alexandria::Preferences.instance.send(message,
112 new_value)
113 self.value = new_value
114 end
115
116 def provider_name
117 @provider.name.downcase
118 end
119
120 def mandatory?
121 @mandatory
122 end
123 end
124
125 def add(*args)
126 self << Variable.new(@provider, *args)
127 end
128
129 def [](obj)
130 case obj
131 when String
132 var = variable_named(obj)
133 var ? var.value : nil
134 when Integer
135 super(obj)
136 end
137 end
138
139 def variable_named(name)
140 self.find { |var| var.name == name }
141 end
142
143 def read
144 self.each do |var|
145 message = @provider.variable_name(var)
146 val = Alexandria::Preferences.instance.send(message)
147 var.value = val unless (val.nil? or (val == "" and var.mandatory?))
148 end
149 end
150 end
151
152 class AbstractProvider
153 attr_reader :prefs
154 attr_accessor :name, :fullname
155
156 def initialize(name, fullname=nil)
157 @name = name
158 @fullname = (fullname or name)
159 @prefs = Preferences.new(self)
160 end
161
162 def reinitialize(fullname)
163 @name << '_' << fullname.hash.to_s
164 @fullname = fullname
165 prefs = Alexandria::Preferences.instance
166 ary = prefs.abstract_providers
167 ary ||= []
168 ary << @name
169 prefs.abstract_providers = ary
170 message = variable_name('name') + '='
171 prefs.send(message, @fullname)
172 end
173
174 def remove
175 prefs = Alexandria::Preferences.instance
176 if ary = prefs.abstract_providers
177 ary.delete(@name)
178 prefs.abstract_providers = ary
179 end
180 if ary = prefs.providers_priority and ary.include?(@name)
181 ary.delete(@name)
182 prefs.providers_priority = ary
183 end
184 self.prefs.each do |variable|
185 name = variable_name(variable)
186 prefs.remove_preference(name)
187 end
188 name = variable_name('name')
189 prefs.remove_preference(name)
190 end
191
192 def variable_name(object)
193 s = case object
194 when String
195 object
196 when Preferences::Variable
197 object.name
198 else
199 raise
200 end
201 @name.downcase + '_' + s
202 end
203
204 def transport
205 config = Alexandria::Preferences.instance.http_proxy_config
206 config ? Net::HTTP.Proxy(*config) : Net::HTTP
207 end
208
209 def abstract?
210 self.class.abstract?
211 end
212
213 def self.abstract?
214 (not self.included_modules.include?(Singleton))
215 end
216
217 def <=>(provider)
218 self.fullname <=> provider.fullname
219 end
220
221 def self.unabstract
222 include Singleton
223 undef_method :reinitialize
224 undef_method :name=
225 undef_method :fullname=
226 undef_method :remove
227 end
228 end
229
230 class GenericProvider < AbstractProvider
231 unabstract
232 end
233
234 require 'alexandria/book_providers/bn'
235 require 'alexandria/book_providers/proxis'
236 require 'alexandria/book_providers/mcu'
237 require 'alexandria/book_providers/thalia'
238 require 'alexandria/book_providers/ibs_it'
239 require 'alexandria/book_providers/renaud'
240 require 'alexandria/book_providers/adlibris'
241 require 'alexandria/book_providers/ls'
242 require 'alexandria/book_providers/bol_it'
243 require 'alexandria/book_providers/webster_it'
244 require 'alexandria/book_providers/worldcat'
245
246 begin
247 require 'alexandria/book_providers/amazon'
248 rescue LoadError
249 puts "Can't load Ruby/Amazon, hence provider Amazon not available"
250 end
251
252 # mechanize is optional
253 begin
254 require 'alexandria/book_providers/dea_store_it'
255 rescue LoadError
256 puts "Can't load mechanize, hence provider Deastore not available"
257 end
258
259 # Ruby/ZOOM is optional
260 begin
261 require 'alexandria/book_providers/z3950'
262 rescue LoadError
263 puts "Can't load Ruby/ZOOM, hence Z39.50 and providers Library of Congress, British Library not available"
264 end
265
266 attr_reader :abstract_classes
267
268 def initialize
269 @prefs = Alexandria::Preferences.instance
270 @abstract_classes = []
271 update_priority
272 end
273
274 def update_priority
275
276 # This is weird code that sorts through the list of classes brought
277 # in by requires and sorts through whether they are 'Abstract' or not,
278 # adding their names to @prefs.
279
280 @abstract_classes.clear
281 providers = {}
282 self.class.constants.each do |constant|
283 next unless md = /(.+)Provider$/.match(constant)
284 klass = self.class.module_eval(constant)
285 if klass.ancestors.include?(AbstractProvider) and
286 klass != GenericProvider and
287 klass != AbstractProvider
288
289 if klass.abstract?
290 @abstract_classes << klass
291 else
292 providers[md[1]] = klass.instance
293 end
294 end
295 end
296 if ary = @prefs.abstract_providers
297 ary.each do |name|
298 md = /^(.+)_/.match(name)
299 next unless md
300 klass_name = md[1] + 'Provider'
301 klass = @abstract_classes.find { |x| x.name.include?(klass_name) }
302 next unless klass
303 fullname = @prefs.send(name.downcase + '_name')
304 next unless fullname
305 instance = klass.new
306 instance.name = name
307 instance.fullname = fullname
308 instance.prefs.read
309 providers[name] = instance
310 end
311 end
312 self.clear
313 priority = (@prefs.providers_priority or [])
314 priority.map! { |x| x.strip }
315 rest = providers.keys - priority
316 priority.each { |pname| self << providers[pname] }
317 rest.sort.each { |pname| self << providers[pname] }
318 self.compact!
319 end
320
321 def self.method_missing(id, *args, &block)
322 self.instance.method(id).call(*args, &block)
323 end
324 end
325 end
Generated using the rcov code coverage analysis tool for Ruby version 0.8.0.