Skip to content

Commit c992671

Browse files
committed
Further refining Instance vs. Class provider opts
Based on feedback concerning the prior commit, this commit seeks to further insulate the change of behavior to downstream implementors. 1) I added discussion and documentation about the change 2) I updated the tests accordingly 3) I added fall-back behavior in case someone inherited from OIA::Provider but did not initialize with `super` The method documentation for `OAI::Provider::Base#provider_context` highlights the entirity of this change: > The traditional interaction of a Provider has been to: > > 1. Assign attributes to the Provider class > 2. Instantiate the Provider class > 3. Call response instance methods for theProvider which pass > the Provider class and not the instance. > > The above behavior continues unless you initialize the Provider with > `{ :provider_context => :instance_based }`. If you do that, then the > > Provider behavior will be: > > 1. Assign attributes to Provider class > 2. Instantiate the Provider class > 3. Call response instance methods for theProvider which pass an > instance of the Provider to those response objects. > a. The instance will mirror all of the assigned Provider class > attributes, but allows for overriding and extending on a > case by case basis. > > (Dear reader, please note the second behavior is something most > of us would've assumed to be the case, but for historic now lost > reasons is not the case.)
1 parent eae3c6c commit c992671

File tree

3 files changed

+113
-16
lines changed

3 files changed

+113
-16
lines changed

lib/oai/provider.rb

Lines changed: 99 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,35 @@
139139
#
140140
# Special thanks to Jose Hales-Garcia for this solution.
141141
#
142+
# ### Leverage the Provider instance
143+
#
144+
# The traditional implementation of the OAI::Provider would pass the OAI::Provider
145+
# class to the different resposnes. This made it hard to inject context into a
146+
# common provider. Consider that we might have different request headers that
147+
# change the scope of the OAI::Provider queries.
148+
#
149+
# ```ruby
150+
# class InstanceProvider
151+
# def initialize(options = {})
152+
# super({ :provider_context => :instance_based })
153+
# @controller = options.fetch(:controller)
154+
# end
155+
# attr_reader :controller
156+
# end
157+
#
158+
# class OaiController < ApplicationController
159+
# def index
160+
# provider = InstanceProvider.new({ :controller => self })
161+
# request_body = provider.process_request(oai_params.to_h)
162+
# render :body => request_body, :content_type => 'text/xml'
163+
# end
164+
# ```
165+
#
166+
# In the above example, the underlying response object will now receive an
167+
# instance of the InstanceProvider. Without the `super({ :provider_context => :instance_based })`
168+
# the response objects would have received the class InstanceProvider as the
169+
# given provider.
170+
#
142171
# ## Supporting custom metadata formats
143172
#
144173
# See {OAI::MetadataFormat} for details.
@@ -240,10 +269,9 @@ module OAI::Provider
240269
class Base
241270
include OAI::Provider
242271

243-
OAI_PMH_ATTRIBUTES = [:name, :url, :prefix, :email, :delete_support, :granularity, :model, :identifier, :description]
244272
class << self
245273
attr_reader :formats
246-
attr_accessor(*OAI_PMH_ATTRIBUTES)
274+
attr_accessor :name, :url, :prefix, :email, :delete_support, :granularity, :model, :identifier, :description
247275

248276
def register_format(format)
249277
@formats ||= {}
@@ -298,19 +326,43 @@ def inherited(klass)
298326
:instance_based => :instance_based
299327
}
300328

301-
def initialize(provider_context = :class_based)
329+
def initialize(options = {})
330+
provider_context = options.fetch(:provider_context) { :class_based }
302331
@provider_context = PROVIDER_CONTEXTS.fetch(provider_context)
303-
OAI_PMH_ATTRIBUTES.each do |attr|
304-
instance_variable_set("@#{attr}", self.class.public_send(attr))
305-
end
306332
end
307-
attr_accessor(*OAI_PMH_ATTRIBUTES)
308333

334+
# @note These are the accessor methods on the class. If you need to overwrite
335+
# them on the instance level you can do that. However, an instance of this
336+
# class won't be used unless you initialize with:
337+
# { :provider_context => :instance_based }
338+
attr_writer :name, :url, :prefix, :email, :delete_support, :granularity, :model, :identifier, :description
339+
340+
# The traditional interaction of a Provider has been to:
341+
#
342+
# 1) Assign attributes to the Provider class
343+
# 2) Instantiate the Provider class
344+
# 3) Call response instance methods for theProvider which pass
345+
# the Provider class and not the instance.
346+
#
347+
# The above behavior continues unless you initialize the Provider with
348+
# { :provider_context => :instance_based }. If you do that, then the
349+
# Provider behavior will be:
350+
#
351+
# 1) Assign attributes to Provider class
352+
# 2) Instantiate the Provider class
353+
# 3) Call response instance methods for theProvider which pass an
354+
# instance of the Provider to those response objects.
355+
# a) The instance will mirror all of the assigned Provider class
356+
# attributes, but allows for overriding and extending on a
357+
# case by case basis.
358+
# (Dear reader, please note the second behavior is something most
359+
# of us would've assumed to be the case, but for historic now lost
360+
# reasons is not the case.)
309361
def provider_context
310-
if @provider_context == :class_based
311-
self.class
312-
else
362+
if @provider_context == :instance_based
313363
self
364+
else
365+
self.class
314366
end
315367
end
316368

@@ -326,6 +378,42 @@ def formats
326378
self.class.formats
327379
end
328380

381+
def name
382+
@name || self.class.name
383+
end
384+
385+
def url
386+
@url || self.class.url
387+
end
388+
389+
def prefix
390+
@prefix || self.class.prefix
391+
end
392+
393+
def email
394+
@email || self.class.email
395+
end
396+
397+
def delete_support
398+
@delete_support || self.class.delete_support
399+
end
400+
401+
def granularity
402+
@granularity || self.class.granularity
403+
end
404+
405+
def model
406+
@model || self.class.model
407+
end
408+
409+
def identifier
410+
@identifier || self.class.identifier
411+
end
412+
413+
def description
414+
@description || self.class.description
415+
end
416+
329417
# Equivalent to '&verb=Identify', returns information about the repository
330418
def identify(options = {})
331419
Response::Identify.new(provider_context, options).to_xml
@@ -370,7 +458,7 @@ def process_request(params = {})
370458
begin
371459

372460
# Allow the request to pass in a url
373-
provider_context.url = params['url'] ? params.delete('url') : self.class.url
461+
provider_context.url = params['url'] ? params.delete('url') : self.url
374462

375463
verb = params.delete('verb') || params.delete(:verb)
376464

test/provider/tc_instance_provider.rb

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ class TestInstanceProvider < Test::Unit::TestCase
1313
# response object.
1414
def test_instance_used_in_responses
1515
@url_path = "/stringy-mc-string-face"
16-
@instance_provider = InstanceProvider.new(:instance_based, @url_path)
16+
@instance_provider = InstanceProvider.new({ :provider_context => :instance_based, :url_path => @url_path })
1717

1818
xml = @instance_provider.identify
1919
doc = REXML::Document.new(xml)
@@ -22,7 +22,16 @@ def test_instance_used_in_responses
2222

2323
def test_class_used_in_responses
2424
@url_path = "/stringy-mc-string-face"
25-
@instance_provider = InstanceProvider.new(:class_based, @url_path)
25+
@instance_provider = InstanceProvider.new({ :provider_context => :class_based, :url_path => @url_path })
26+
27+
xml = @instance_provider.identify
28+
doc = REXML::Document.new(xml)
29+
assert_equal "http://localhost", doc.elements["OAI-PMH/Identify/baseURL"].text
30+
end
31+
32+
def test_by_default_class_used_in_responses
33+
@url_path = "/stringy-mc-string-face"
34+
@instance_provider = InstanceProvider.new({ :url_path => @url_path })
2635

2736
xml = @instance_provider.identify
2837
doc = REXML::Document.new(xml)

test/provider/test_helper_provider.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,9 @@ class InstanceProvider < Provider::Base
5050
repository_url 'http://localhost'
5151
source_model SimpleModel.new
5252

53-
def initialize(context_provider, url_path)
54-
super(context_provider)
55-
@url_path = url_path
53+
def initialize(options = {})
54+
super
55+
@url_path = options.fetch(:url_path)
5656
end
5757
attr_reader :url_path
5858

0 commit comments

Comments
 (0)