Class: Service
- Inherits:
-
Object
- Object
- Service
- Defined in:
- lib/service.rb
Overview
Services are defined from the config/umlaut_config/services.yml file. hey should have the following properties id : unique internal string for the service, unique in yml file display_name : user displayable string url : A base url of some kind used by specific service type : Class name of class found in lib/service_adaptors to be used for logic priority: 0-9 (foreground) or a-z (background) for order of service operation
Specific service_adaptor classes may have specific addtional configuration, commonly including 'password' or 'api_key'. specific service can put " required_config_parms :param1, :param2" in definition, for requirement exception raising on initialize.
Service Sub-classes
Can include required config params in the class definition, eg:
required_config_params :api_key, :base_url
Should define #service_types_generated returning an array of ServiceTypeValues. This is neccesary for the Service to be run as a background service, and have the auto background updater work.
The vast majority of services are 'standard' services, however there are other 'tasks' that a service can be. Well, right now, one other, 'link_out_filter'. The services 'task' config property sets the task/function/hook of the service. Default is 'standard'.
A standard service defines handle(request)
A link_out_filter service defines link_out_filter(request, url). If service returns a new url from filter_url, that's the url the user will be directed to. If service returns original url or nil, original url will still be used.
Direct Known Subclasses
AjaxExport, Amazon, Blacklight, BookFinder, Bx, Connotea, CoverThing, Crossref, Delicious, ElsevierCover, Ezproxy, GoogleBookSearch, GoogleSearch, Gpo, HathiTrust, Hip3Service, InternetArchive, InternetArchiveScraper, IsbnDb, Isi, Jcr, JstorCover, MBooks, OldGoogleBookSearch, Opac, OpenLibrary, OpenLibraryCover, OpencontentSearch, PrimoService, Pubmed, RequestToFixture, Scopus, Sfx, SfxBackchannelRecord, UlrichsCover, UlrichsLink, Worldcat, WorldcatIdentities, YahooMyWeb, YahooSearch
Defined Under Namespace
Classes: FakeSession
Constant Summary
- StandardTask =
Some constants for 'function' values
'standard'- LinkOutFilterTask =
'link_out_filter'- @@required_params_for_subclass =
initialize class var
{}
Instance Attribute Summary (collapse)
-
- (Object) name
readonly
Returns the value of attribute name.
-
- (Object) priority
readonly
Returns the value of attribute priority.
-
- (Object) request
Returns the value of attribute request.
-
- (Object) service_id
readonly
Returns the value of attribute service_id.
- - (Object) session_id
-
- (Object) status
readonly
Returns the value of attribute status.
-
- (Object) task
readonly
Returns the value of attribute task.
-
- (Object) url
readonly
Returns the value of attribute url.
Class Method Summary (collapse)
-
+ (Object) required_config_params(*params)
Sub-class can call class method like:.
Instance Method Summary (collapse)
- - (Object) display_name
-
- (Object) handle(request)
Implemented by sub-class.
-
- (Object) handle_wrapper(request)
Method that should actually be called to trigger the service.
-
- (Service) initialize(config)
constructor
A new instance of Service.
-
- (Object) link_out_filter(orig_url, service_type, other_args = {})
This method is implemented by a concrete sub-class meant to fulfill the task:link_out_filter.
-
- (Object) preempted_by(uml_request, for_type_generated = nil)
Pre-emption hashes specify a combination of existing responses or service executions that can pre-empt this service.
-
- (Object) response_to_view_data(service_response)
Default implementation to take a ServiceResponse and parse into a hash of values useful to the view.
-
- (Object) response_url(service_type, submitted_params)
This method is called by Umlaut when user clicks on a service response.
-
- (Object) service_types_generated
Must be implemented by concrete sub-class.
-
- (Object) session
Returns a read-only version of the session hash.
-
- (Object) update_session(new_values)
If we just allowed changes to our session hash, and rewrote it to the store, we'd get a race condition where we could over-write another service's changes.
-
- (Object) view_data_from_service_type(service_type_obj)
Pass this method a ServiceType object, it will return a hash-like object of display values, for the view.
Constructor Details
- (Service) initialize(config)
A new instance of Service
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
# File 'lib/service.rb', line 45 def initialize(config) config.each do | key, val | self.instance_variable_set(('@'+key).to_sym, val) end # task defaults to standard @task ||= StandardTask # check required params, and throw if neccesary required_params = Array.new # Some things required for all services required_params << "priority" # Custom things for this particular sub-class required_params.concat( @@required_params_for_subclass[self.class.name] ) if @@required_params_for_subclass[self.class.name] required_params.each do |param| begin value = self.instance_variable_get('@' + param.to_s) # docs say it raises a nameerror if it doesn't exist, docs # lie. So we'll just raise one ourselves, and catch it, to # handle both cases. raise NameError if value.nil? rescue NameError raise ArgumentError.new("Missing Service configuration parameter. Service type #{self.class} (id: #{self.id}) requires a config parameter named '#{param}'. Check your config/umlaut_config/services.yml file.") end end end |
Instance Attribute Details
- (Object) name (readonly)
Returns the value of attribute name
35 36 37 |
# File 'lib/service.rb', line 35 def name @name end |
- (Object) priority (readonly)
Returns the value of attribute priority
35 36 37 |
# File 'lib/service.rb', line 35 def priority @priority end |
- (Object) request
Returns the value of attribute request
37 38 39 |
# File 'lib/service.rb', line 37 def request @request end |
- (Object) service_id (readonly)
Returns the value of attribute service_id
35 36 37 |
# File 'lib/service.rb', line 35 def service_id @service_id end |
- (Object) session_id
173 174 175 176 177 178 179 180 |
# File 'lib/service.rb', line 173 def session_id unless (@session_id) raise Exception.new("This service has not been initialized with a request!") unless request @session_id = request.session_id end return @session_id end |
- (Object) status (readonly)
Returns the value of attribute status
35 36 37 |
# File 'lib/service.rb', line 35 def status @status end |
- (Object) task (readonly)
Returns the value of attribute task
35 36 37 |
# File 'lib/service.rb', line 35 def task @task end |
- (Object) url (readonly)
Returns the value of attribute url
35 36 37 |
# File 'lib/service.rb', line 35 def url @url end |
Class Method Details
+ (Object) required_config_params(*params)
Sub-class can call class method like:
required_config_params :symbol1, :symbol2, symbol3
in class definition body. List of config parmas that are required, exception will be thrown if not present.
162 163 164 165 166 167 168 169 170 |
# File 'lib/service.rb', line 162 def self.required_config_params(*params) params.each do |p| # Key on name of specific sub-class. Since this is a class # method, that should be self.name @@required_params_for_subclass[self.name] ||= Array.new a = @@required_params_for_subclass[self.name] a.push( p ) unless a.include?( p ) end end |
Instance Method Details
- (Object) display_name
121 122 123 124 125 |
# File 'lib/service.rb', line 121 def display_name # If no display_name is set, default to the id string. Not a good idea, # but hey. return @display_name ||= self.service_id end |
- (Object) handle(request)
Implemented by sub-class. Standard response-generating services implement this method to do their work, generate responses and/or metadata.
102 103 104 |
# File 'lib/service.rb', line 102 def handle(request) raise Exception.new("handle() must be implemented by Service concrete sub-class, for standard services!") end |
- (Object) handle_wrapper(request)
Method that should actually be called to trigger the service. Will check pre-emption.
90 91 92 93 94 95 96 97 98 |
# File 'lib/service.rb', line 90 def handle_wrapper(request) unless ( preempted_by(request) ) return handle(request) else # Pre-empted, log and close dispatch record as 'succesful'. RAILS_DEFAULT_LOGGER.debug("Service #{service_id} was pre-empted and not run.") return request.dispatched(self, true) end end |
- (Object) link_out_filter(orig_url, service_type, other_args = {})
This method is implemented by a concrete sub-class meant to fulfill the task:link_out_filter. Will be called when the user clicks on a url that will redirect external to Umlaut. The link_out_filter service has the ability to intervene and record and/or change the url. link_out_filters are called in order of priority config param assigned, 0 through 9.
orig_url is the current url umlaut is planning on sending the user to. service_type is the ServiceType object responsible for this url. the third argument is reserved for future use an options hash.
116 117 118 |
# File 'lib/service.rb', line 116 def link_out_filter(orig_url, service_type, other_args = {}) raise Exception.new("#link_out_filter must be implemented by Service concrete sub-class with task link_out_filter!") end |
- (Object) preempted_by(uml_request, for_type_generated = nil)
Pre-emption hashes specify a combination of existing responses or service executions that can pre-empt this service. Can specify a service, a response type (ServiceTypeValue), or a combination of both.
service's preempted_by property can either be a single pre-emption hash, or an array of pre-emption hashes.
Can also specify that pre-emption is only of a certain service type generated by self.
The Service base class will enforce pre-emption and not even run a service at all *so long as self_type is nil or '*' *. If the pre-emption only applies to certain types generated by the service and not the entire execution of the service, the concrete service subclass must implement logic to do that. Calling the preempted method with the second argument set will be helpful in writing this logic.
A preemption hash has string keys:
existing_service: id of service that will pre-empt this service.
If key does not exist or is "*", then not specified,
any service. (existing_type will be specified).
existing_type: ServiceTypeValue name that pre-empts this
service. "+" means that the service specified
in existing_service must have generated some
response, but type does not matter. "*" means
that the service specified in existing_service
must have completed succesfully, but may not
have generated any responses.
self_type: If blank or "*", preemption applies to any running
of this service at all. If set to a ServiceTypeValue
name, pre-emption is only of certain types generated
by this service.
289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 |
# File 'lib/service.rb', line 289 def preempted_by(uml_request, for_type_generated=nil) preempted_by = @preempted_by return false if preempted_by.nil? preempted_by = [preempted_by] unless preempted_by.kind_of?(Array) preemption = nil preempted_by.each do | hash | service = hash["existing_service"] || "*" other_type = hash["existing_type"] || "*" self_type = hash["self_type"] || "*" next unless (self_type == "*" || self_type == for_type_generated) if (other_type == "*") # Need to check dispatched services instead of service_types, # as we pre-empt even if no services created. preemption = uml_request.dispatched_services.to_a.find do |disp| service == "*" || (disp.service_id == service && (disp.status == DispatchedService.Succesful )) end else # Check service types preemption = uml_request.service_types.to_a.find do |st| ( other_type == "*" || other_type == "+" || st.service_type_value.name == other_type) && ( service == "*" || st.service_response.service.service_id == service) end end break if preemption end return (! preemption.nil? ) end |
- (Object) response_to_view_data(service_response)
Default implementation to take a ServiceResponse and parse into a hash of values useful to the view. Basic implementation just asks ServiceResposne for it's data_values object, which contains all ServiceResponse data (including arbitrary keys serialized in the hash) in an object with the hash accessor method [] .
154 155 156 |
# File 'lib/service.rb', line 154 def response_to_view_data(service_response) return service_response.data_values end |
- (Object) response_url(service_type, submitted_params)
This method is called by Umlaut when user clicks on a service response. Default implementation here just returns response. You can over-ride in a sub-class to provide custom implementation of on-demand url generation. Second argument is the http request params sent by the client, used for service types that take form submissions (eg search_inside). Should return a String url.
250 251 252 253 254 |
# File 'lib/service.rb', line 250 def response_url(service_type, submitted_params ) url = service_type.service_response[:url] raise "No url provided by service response" if url.nil? || url.empty? return url end |
- (Object) service_types_generated
Must be implemented by concrete sub-class. return an Array of ServiceTypeValues constituting the types of ServiceResponses the service might generate. Used by Umlaut infrastructure including the background service execution scheme itslef, as well asxml services returning information on services in progress.
Example for a service that only generates fulltext:
return [ ServiceTypeValue[:fulltext] ]
83 84 85 |
# File 'lib/service.rb', line 83 def service_types_generated raise Exception.new("service_types_generated() must be implemented by Service concrete sub-class!") end |
- (Object) session
Returns a read-only version of the session hash. Lazy loaded. See #update_session for making changes. Will have to be fixed for Rails 2.3.
196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 |
# File 'lib/service.rb', line 196 def session #lazy load unless (@session_data || session_id.blank? ) # Craziness to restore a session in Rails pre 2.2. Will definitely # need to be changed for Rails 2.3 fake_cgi_session = FakeSession.new(session_id) @session_store_obj = ActionController::Base.session_store.new(fake_cgi_session) @_session_data = @session_store_obj.restore # modifications here are not going to be automatically stored, # so don't do them. See #update_session instead. @session_data = @_session_data.clone @session_data.freeze end if ( session_id.blank? && @session_data.nil?) raise Exception.new("No session_id is available, therefore no session is available.") end return @session_data end |
- (Object) update_session(new_values)
If we just allowed changes to our session hash, and rewrote it to the store, we'd get a race condition where we could over-write another service's changes. Rails itself is actually subject to that too. So, you tell us exactly which values you want to update, we'll refetch a fresh session, and save it with your changes.
We're not actually guarding against the race condition, just fetching and storing quickly and hoping to miss it. FIXME.
example: update_session( :new_value => "foo", :other => "bar")
Will have to be fixed for Rails 2.3.
231 232 233 234 235 236 237 238 239 240 241 |
# File 'lib/service.rb', line 231 def update_session(new_values) # force a new fetch @session = nil session #and update that guy, with our mutable version @_session_data.merge!(new_values) @session_store_obj.close # and update our cached copy to have the changes. @session_data = @_session_data.clone @session_data.freeze end |
- (Object) view_data_from_service_type(service_type_obj)
Pass this method a ServiceType object, it will return a hash-like object of display values, for the view. Implementation is usually in sub-class, by means of a set of methods "to_[service type name]" implemented in sub-class . parseResponse will find those. Subclasses will not generally override view_data_from_service_type, although they can for complete custom handling. Make sure to return a Hash or hash-like (duck-typed) object.
134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
# File 'lib/service.rb', line 134 def view_data_from_service_type(service_type_obj) service_type_code = service_type_obj.service_type_value.name service_response = service_type_obj.service_response begin # try to call a method named "to_#{service_type_code}", implemented by sub-class self.send("to_#{service_type_code}", service_response) rescue NoMethodError # No to_#{response_type} method? How about the catch-all method? # If not implemented in sub-class, we have a VERY basic # default implementation in this class. self.send("response_to_view_data", service_response) end end |