Skip to content

Commit 5e4e4b5

Browse files
authored
Merge pull request #76 from code4lib/configured_id_column
Add configurable `identifier_field` analagous to existing `timestamp_field`.
2 parents cad2f9f + f99ef8e commit 5e4e4b5

File tree

6 files changed

+54
-26
lines changed

6 files changed

+54
-26
lines changed

lib/oai/provider.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,8 +155,10 @@
155155
#
156156
# Valid options include:
157157
#
158-
# * `timestamp_field` - Specifies the model field to use as the update
158+
# * `timestamp_field` - Specifies the model field/method to use as the update
159159
# filter. Defaults to `updated_at`.
160+
# * `identifier_field` -- specifies the model field/method to use to get value to use
161+
# as oai identifier (method return value should not include prefix)
160162
# * `limit` - Maximum number of records to return in each page/set.
161163
# Defaults to 100, set to `nil` for all records in one page. Otherwise
162164
# the wrapper will paginate the result via resumption tokens.

lib/oai/provider/model.rb

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
11
module OAI::Provider
22
# = OAI::Provider::Model
33
#
4-
# Model implementers should subclass OAI::Provider::Model and override
4+
# Model implementers should subclass OAI::Provider::Model and override
55
# Model#earliest, Model#latest, and Model#find. Optionally Model#sets and
6-
# Model#deleted? can be used to support sets and record deletions. It
7-
# is also the responsibility of the model implementer to account for
8-
# resumption tokens if support is required. Models that don't support
9-
# resumption tokens should raise an exception if a limit is requested
6+
# Model#deleted? can be used to support sets and record deletions. It
7+
# is also the responsibility of the model implementer to account for
8+
# resumption tokens if support is required. Models that don't support
9+
# resumption tokens should raise an exception if a limit is requested
1010
# during initialization.
1111
#
1212
# earliest - should return the earliest update time in the repository.
1313
# latest - should return the most recent update time in the repository.
1414
# sets - should return an array of sets supported by the repository.
1515
# deleted? - individual records returned should respond true or false
1616
# when sent the deleted? message.
17-
# available_formats - if overridden, individual records should return an
18-
# array of prefixes for all formats in which that record is available,
17+
# available_formats - if overridden, individual records should return an
18+
# array of prefixes for all formats in which that record is available,
1919
# if other than ["oai_dc"]
2020
# about - if overridden, should return a String or Array of XML Strings to
2121
# insert into the OAI Record <about> chunks.
@@ -28,29 +28,29 @@ module OAI::Provider
2828
# There are several helper models for dealing with resumption tokens please
2929
# see the ResumptionToken class for more details.
3030
#
31-
3231
class Model
33-
attr_reader :timestamp_field
34-
35-
def initialize(limit = nil, timestamp_field = 'updated_at')
32+
attr_reader :timestamp_field, :identifier_field
33+
34+
def initialize(limit = nil, timestamp_field = 'updated_at', identifier_field = 'id')
3635
@limit = limit
36+
@identifier_field = identifier_field
3737
@timestamp_field = timestamp_field
3838
end
3939

4040
# should return the earliest timestamp available from this model.
4141
def earliest
4242
raise NotImplementedError.new
4343
end
44-
44+
4545
# should return the latest timestamp available from this model.
4646
def latest
4747
raise NotImplementedError.new
4848
end
49-
49+
5050
def sets
5151
nil
5252
end
53-
53+
5454
# find is the core method of a model, it returns records from the model
5555
# bases on the parameters passed in.
5656
#
@@ -61,12 +61,12 @@ def sets
6161
# * :from => earliest timestamp to be included in the results
6262
# * :until => latest timestamp to be included in the results
6363
# * :set => the set from which to retrieve the results
64-
# * :metadata_prefix => type of metadata requested (this may be useful if
64+
# * :metadata_prefix => type of metadata requested (this may be useful if
6565
# not all records are available in all formats)
6666
def find(selector, options={})
6767
raise NotImplementedError.new
6868
end
69-
69+
7070
def deleted?
7171
false
7272
end
@@ -76,5 +76,5 @@ def about record
7676
nil
7777
end
7878
end
79-
79+
8080
end

lib/oai/provider/model/activerecord_wrapper.rb

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,18 @@ module OAI::Provider
1010
#
1111
class ActiveRecordWrapper < Model
1212

13-
attr_reader :model, :timestamp_field
14-
13+
attr_reader :model, :timestamp_field, :identifier_field
14+
15+
# If custom 'timestamp_field' is used, be aware this will be an ActiveRecord
16+
# attribute that we will limit on, so perhaps should be indexe appropriately.
17+
#
18+
# If custom `identifier_field` is used, be aware this will be an ActiveRecord
19+
# attribute that we will sort on, and use in WHERE clauses with `=` as well as
20+
# greater than/less than, so should be indexed appropriately.
1521
def initialize(model, options={})
1622
@model = model
1723
@timestamp_field = options.delete(:timestamp_field) || 'updated_at'
24+
@identifier_field = options.delete(:identifier_field) || model.primary_key || "id"
1825
@limit = options.delete(:limit) || 100
1926

2027
unless options.empty?
@@ -54,7 +61,7 @@ def find(selector, options={})
5461
find_scope.where(conditions)
5562
end
5663
else
57-
find_scope.where(conditions).find(selector)
64+
find_scope.where(conditions).find_by!(identifier_field => selector)
5865
end
5966
end
6067

@@ -129,7 +136,7 @@ def next_set(find_scope, token_string)
129136
else # end of result set
130137
find_scope.where(token_conditions(token))
131138
.limit(@limit)
132-
.order("#{model.primary_key} asc")
139+
.order("#{identifier_field} asc")
133140
end
134141
end
135142

@@ -138,9 +145,9 @@ def next_set(find_scope, token_string)
138145
def select_partial(find_scope, token)
139146
records = find_scope.where(token_conditions(token))
140147
.limit(@limit)
141-
.order("#{model.primary_key} asc")
148+
.order("#{identifier_field} asc")
142149
raise OAI::ResumptionTokenException.new unless records
143-
offset = records.last.send(model.primary_key.to_sym)
150+
offset = records.last.send(identifier_field)
144151

145152
PartialResult.new(records, token.next(offset))
146153
end
@@ -157,7 +164,7 @@ def token_conditions(token)
157164

158165
return sql if 0 == last
159166
# Now add last id constraint
160-
sql.first << " AND #{model.primary_key} > :id"
167+
sql.first << " AND #{identifier_field} > :id"
161168
sql.last[:id] = last
162169

163170
return sql

lib/oai/provider/response/record_response.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ def about_for(record)
5050

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

5656
def timestamp_for(record)

test/activerecord_provider/helpers/providers.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@ class ARProvider < OAI::Provider::Base
1212
source_model ActiveRecordWrapper.new(DCField)
1313
end
1414

15+
class ARProviderCustomIdentifierField < OAI::Provider::Base
16+
repository_name 'ActiveRecord Based Provider'
17+
repository_url 'http://localhost'
18+
record_prefix 'oai:test'
19+
source_model ActiveRecordWrapper.new(DCField, identifier_field: "source")
20+
end
21+
1522
class ARProviderWithScope < OAI::Provider::Base
1623
DATE_LESS_THAN_RESTRICTION = Time.parse("2007-03-12 19:30:22 UTC")
1724

test/activerecord_provider/tc_ar_provider.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,18 @@ def test_list_records_scope
4040
assert_equal expected_count, doc.elements['OAI-PMH/ListRecords'].to_a.size
4141
end
4242

43+
44+
def test_get_record_alternate_identifier_column
45+
@provider = ARProviderCustomIdentifierField.new
46+
47+
record_id = DCField.first.send(@provider.class.model.identifier_field)
48+
49+
doc = REXML::Document.new(@provider.get_record(
50+
:identifier => "oai:test:#{record_id}", :metadata_prefix => 'oai_dc'))
51+
52+
assert_equal "oai:test:#{record_id}", doc.elements['OAI-PMH/GetRecord/record/header/identifier'].text
53+
end
54+
4355
def test_list_identifiers
4456
assert_nothing_raised { REXML::Document.new(@provider.list_identifiers) }
4557
doc = REXML::Document.new(@provider.list_identifiers)

0 commit comments

Comments
 (0)