Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion lib/oai/provider.rb
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,10 @@
#
# Valid options include:
#
# * `timestamp_field` - Specifies the model field to use as the update
# * `timestamp_field` - Specifies the model field/method to use as the update
# filter. Defaults to `updated_at`.
# * `identifier_field` -- specifies the model field/method to use to get value to use
# as oai identifier (method return value should not include prefix)
# * `limit` - Maximum number of records to return in each page/set.
# Defaults to 100, set to `nil` for all records in one page. Otherwise
# the wrapper will paginate the result via resumption tokens.
Expand Down
34 changes: 17 additions & 17 deletions lib/oai/provider/model.rb
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
module OAI::Provider
# = OAI::Provider::Model
#
# Model implementers should subclass OAI::Provider::Model and override
# Model implementers should subclass OAI::Provider::Model and override
# Model#earliest, Model#latest, and Model#find. Optionally Model#sets and
# Model#deleted? can be used to support sets and record deletions. It
# is also the responsibility of the model implementer to account for
# resumption tokens if support is required. Models that don't support
# resumption tokens should raise an exception if a limit is requested
# Model#deleted? can be used to support sets and record deletions. It
# is also the responsibility of the model implementer to account for
# resumption tokens if support is required. Models that don't support
# resumption tokens should raise an exception if a limit is requested
# during initialization.
#
# earliest - should return the earliest update time in the repository.
# latest - should return the most recent update time in the repository.
# sets - should return an array of sets supported by the repository.
# deleted? - individual records returned should respond true or false
# when sent the deleted? message.
# available_formats - if overridden, individual records should return an
# array of prefixes for all formats in which that record is available,
# available_formats - if overridden, individual records should return an
# array of prefixes for all formats in which that record is available,
# if other than ["oai_dc"]
# about - if overridden, should return a String or Array of XML Strings to
# insert into the OAI Record <about> chunks.
Expand All @@ -28,29 +28,29 @@ module OAI::Provider
# There are several helper models for dealing with resumption tokens please
# see the ResumptionToken class for more details.
#

class Model
attr_reader :timestamp_field
def initialize(limit = nil, timestamp_field = 'updated_at')
attr_reader :timestamp_field, :identifier_field

def initialize(limit = nil, timestamp_field = 'updated_at', identifier_field = 'id')
@limit = limit
@identifier_field = identifier_field
@timestamp_field = timestamp_field
end

# should return the earliest timestamp available from this model.
def earliest
raise NotImplementedError.new
end

# should return the latest timestamp available from this model.
def latest
raise NotImplementedError.new
end

def sets
nil
end

# find is the core method of a model, it returns records from the model
# bases on the parameters passed in.
#
Expand All @@ -61,12 +61,12 @@ def sets
# * :from => earliest timestamp to be included in the results
# * :until => latest timestamp to be included in the results
# * :set => the set from which to retrieve the results
# * :metadata_prefix => type of metadata requested (this may be useful if
# * :metadata_prefix => type of metadata requested (this may be useful if
# not all records are available in all formats)
def find(selector, options={})
raise NotImplementedError.new
end

def deleted?
false
end
Expand All @@ -76,5 +76,5 @@ def about record
nil
end
end

end
21 changes: 14 additions & 7 deletions lib/oai/provider/model/activerecord_wrapper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,18 @@ module OAI::Provider
#
class ActiveRecordWrapper < Model

attr_reader :model, :timestamp_field

attr_reader :model, :timestamp_field, :identifier_field

# If custom 'timestamp_field' is used, be aware this will be an ActiveRecord
# attribute that we will limit on, so perhaps should be indexe appropriately.
#
# If custom `identifier_field` is used, be aware this will be an ActiveRecord
# attribute that we will sort on, and use in WHERE clauses with `=` as well as
# greater than/less than, so should be indexed appropriately.
def initialize(model, options={})
@model = model
@timestamp_field = options.delete(:timestamp_field) || 'updated_at'
@identifier_field = options.delete(:identifier_field) || model.primary_key || "id"
@limit = options.delete(:limit) || 100

unless options.empty?
Expand Down Expand Up @@ -54,7 +61,7 @@ def find(selector, options={})
find_scope.where(conditions)
end
else
find_scope.where(conditions).find(selector)
find_scope.where(conditions).find_by!(identifier_field => selector)
end
end

Expand Down Expand Up @@ -129,7 +136,7 @@ def next_set(find_scope, token_string)
else # end of result set
find_scope.where(token_conditions(token))
.limit(@limit)
.order("#{model.primary_key} asc")
.order("#{identifier_field} asc")
end
end

Expand All @@ -138,9 +145,9 @@ def next_set(find_scope, token_string)
def select_partial(find_scope, token)
records = find_scope.where(token_conditions(token))
.limit(@limit)
.order("#{model.primary_key} asc")
.order("#{identifier_field} asc")
raise OAI::ResumptionTokenException.new unless records
offset = records.last.send(model.primary_key.to_sym)
offset = records.last.send(identifier_field)

PartialResult.new(records, token.next(offset))
end
Expand All @@ -157,7 +164,7 @@ def token_conditions(token)

return sql if 0 == last
# Now add last id constraint
sql.first << " AND #{model.primary_key} > :id"
sql.first << " AND #{identifier_field} > :id"
sql.last[:id] = last

return sql
Expand Down
2 changes: 1 addition & 1 deletion lib/oai/provider/response/record_response.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def about_for(record)

# Namespace syntax suggested in http://www.openarchives.org/OAI/2.0/guidelines-oai-identifier.htm
def identifier_for(record)
"#{provider.prefix}:#{record.id}"
"#{provider.prefix}:#{record.send( provider.model.identifier_field )}"
end

def timestamp_for(record)
Expand Down
7 changes: 7 additions & 0 deletions test/activerecord_provider/helpers/providers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@ class ARProvider < OAI::Provider::Base
source_model ActiveRecordWrapper.new(DCField)
end

class ARProviderCustomIdentifierField < OAI::Provider::Base
repository_name 'ActiveRecord Based Provider'
repository_url 'http://localhost'
record_prefix 'oai:test'
source_model ActiveRecordWrapper.new(DCField, identifier_field: "source")
end

class ARProviderWithScope < OAI::Provider::Base
DATE_LESS_THAN_RESTRICTION = Time.parse("2007-03-12 19:30:22 UTC")

Expand Down
12 changes: 12 additions & 0 deletions test/activerecord_provider/tc_ar_provider.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,18 @@ def test_list_records_scope
assert_equal expected_count, doc.elements['OAI-PMH/ListRecords'].to_a.size
end


def test_get_record_alternate_identifier_column
@provider = ARProviderCustomIdentifierField.new

record_id = DCField.first.send(@provider.class.model.identifier_field)

doc = REXML::Document.new(@provider.get_record(
:identifier => "oai:test:#{record_id}", :metadata_prefix => 'oai_dc'))

assert_equal "oai:test:#{record_id}", doc.elements['OAI-PMH/GetRecord/record/header/identifier'].text
end

def test_list_identifiers
assert_nothing_raised { REXML::Document.new(@provider.list_identifiers) }
doc = REXML::Document.new(@provider.list_identifiers)
Expand Down