oai 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/oai/provider/model/activerecord_wrapper.rb +3 -3
- data/lib/oai/provider/resumption_token.rb +52 -16
- data/test/activerecord_provider/helpers/providers.rb +7 -0
- data/test/activerecord_provider/tc_simple_paging_provider.rb +19 -0
- data/test/provider/tc_resumption_tokens.rb +6 -0
- metadata +3 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e7cdbec6f4b6ca5a672add7fe1463748bd9f8a9cdf50a55400a5711e45be6ef9
|
4
|
+
data.tar.gz: 6a22c53d9520777e69bb6e32032880cb53793d89a2712a5b7b7370d02553ff25
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1cf4fa083c9c4486110ec0c7322ac077addd66050a3085291b591597c02ec8d02ca8230594e2fc16070d700dd243ec0f3f74d63ca4684522828f6431f15f6bf7
|
7
|
+
data.tar.gz: 8bca0a510ff38a28e0e4119e645759e627a19aca0aed1259b2129592094c4851da7337b0824054660b2b87fd07daf7b529ad7fe30dfe237aa90f9b5a5faac81a
|
@@ -159,13 +159,13 @@ module OAI::Provider
|
|
159
159
|
# the last 'id' of the previous set is used as the
|
160
160
|
# filter to the next set.
|
161
161
|
def token_conditions(token)
|
162
|
-
|
162
|
+
last_id = token.last_str
|
163
163
|
sql = sql_conditions token.to_conditions_hash
|
164
164
|
|
165
|
-
return sql if 0 ==
|
165
|
+
return sql if "0" == last_id
|
166
166
|
# Now add last id constraint
|
167
167
|
sql.first << " AND #{identifier_field} > :id"
|
168
|
-
sql.last[:id] =
|
168
|
+
sql.last[:id] = last_id
|
169
169
|
|
170
170
|
return sql
|
171
171
|
end
|
@@ -8,16 +8,41 @@ module OAI::Provider
|
|
8
8
|
# The ResumptionToken class forms the basis of paging query results. It
|
9
9
|
# provides several helper methods for dealing with resumption tokens.
|
10
10
|
#
|
11
|
+
# OAI-PMH spec does not specify anything about resumptionToken format, they can
|
12
|
+
# be purely opaque tokens.
|
13
|
+
#
|
14
|
+
# Our implementation however encodes everything needed to construct the next page
|
15
|
+
# inside the resumption token.
|
16
|
+
#
|
17
|
+
# == The 'last' component: offset or ID/pk to resume from
|
18
|
+
#
|
19
|
+
# The `#last` component is an offset or ID to resume from. In the case of it being
|
20
|
+
# an ID to resume from, this assumes that ID's are sortable and results are returned
|
21
|
+
# in ID order, so that the 'last' ID can be used as the place to resume from.
|
22
|
+
#
|
23
|
+
# Originally it was assumed that #last was always an integer, but since existing
|
24
|
+
# implementations (like ActiveRecordWrapper) used it as an ID, and identifiers and
|
25
|
+
# primary keys are _not_ always integers (can be UUID etc), we have expanded to allow
|
26
|
+
# any string value.
|
27
|
+
#
|
28
|
+
# However, for backwards compatibility #last always returns an integer (sometimes 0 if
|
29
|
+
# actual last component is not an integer), and #last_str returns the full string version.
|
30
|
+
# Trying to change #last itself to be string broke a lot of existing code in this gem
|
31
|
+
# in mysterious ways.
|
32
|
+
#
|
33
|
+
# Also beware that in some cases the value 0/"0" seems to be a special value used
|
34
|
+
# to signify some special case. A lot of "code archeology" going on here after significant
|
35
|
+
# period of no maintenance to this gem.
|
11
36
|
class ResumptionToken
|
12
|
-
attr_reader :prefix, :set, :from, :until, :last, :expiration, :total
|
37
|
+
attr_reader :prefix, :set, :from, :until, :last, :last_str, :expiration, :total
|
13
38
|
|
14
39
|
# parses a token string and returns a ResumptionToken
|
15
40
|
def self.parse(token_string)
|
16
41
|
begin
|
17
42
|
options = {}
|
18
|
-
matches = /(.+):(
|
19
|
-
options[:last] = matches.captures[1]
|
20
|
-
|
43
|
+
matches = /(.+):([^ :]+)$/.match(token_string)
|
44
|
+
options[:last] = matches.captures[1]
|
45
|
+
|
21
46
|
parts = matches.captures[0].split('.')
|
22
47
|
options[:metadata_prefix] = parts.shift
|
23
48
|
parts.each do |part|
|
@@ -35,7 +60,7 @@ module OAI::Provider
|
|
35
60
|
raise OAI::ResumptionTokenException.new
|
36
61
|
end
|
37
62
|
end
|
38
|
-
|
63
|
+
|
39
64
|
# extracts the metadata prefix from a token string
|
40
65
|
def self.extract_format(token_string)
|
41
66
|
return token_string.split('.')[0]
|
@@ -44,32 +69,32 @@ module OAI::Provider
|
|
44
69
|
def initialize(options, expiration = nil, total = nil)
|
45
70
|
@prefix = options[:metadata_prefix]
|
46
71
|
@set = options[:set]
|
47
|
-
|
72
|
+
self.last = options[:last]
|
48
73
|
@from = options[:from] if options[:from]
|
49
74
|
@until = options[:until] if options[:until]
|
50
75
|
@expiration = expiration if expiration
|
51
76
|
@total = total if total
|
52
77
|
end
|
53
|
-
|
78
|
+
|
54
79
|
# convenience method for setting the offset of the next set of results
|
55
80
|
def next(last)
|
56
|
-
|
81
|
+
self.last = last
|
57
82
|
self
|
58
83
|
end
|
59
|
-
|
84
|
+
|
60
85
|
def ==(other)
|
61
86
|
prefix == other.prefix and set == other.set and from == other.from and
|
62
|
-
self.until == other.until and last == other.last and
|
87
|
+
self.until == other.until and last == other.last and
|
63
88
|
expiration == other.expiration and total == other.total
|
64
89
|
end
|
65
|
-
|
90
|
+
|
66
91
|
# output an xml resumption token
|
67
92
|
def to_xml
|
68
93
|
xml = Builder::XmlMarkup.new
|
69
94
|
xml.resumptionToken(encode_conditions, hash_of_attributes)
|
70
95
|
xml.target!
|
71
96
|
end
|
72
|
-
|
97
|
+
|
73
98
|
# return a hash containing just the model selection parameters
|
74
99
|
def to_conditions_hash
|
75
100
|
conditions = {:metadata_prefix => self.prefix }
|
@@ -78,20 +103,31 @@ module OAI::Provider
|
|
78
103
|
conditions[:until] = self.until if self.until
|
79
104
|
conditions
|
80
105
|
end
|
81
|
-
|
82
|
-
# return the a string representation of the token minus the offset
|
106
|
+
|
107
|
+
# return the a string representation of the token minus the offset/ID
|
108
|
+
#
|
109
|
+
# Q: Why does it eliminate the offset/id "last" on the end? Doesn't fully
|
110
|
+
# represent state without it, which is confusing. Not sure, but
|
111
|
+
# other code seems to rely on it, tests break if not.
|
83
112
|
def to_s
|
84
113
|
encode_conditions.gsub(/:\w+?$/, '')
|
85
114
|
end
|
86
115
|
|
87
116
|
private
|
88
|
-
|
117
|
+
|
118
|
+
# take care of our logic to store an integer and a str version, for backwards
|
119
|
+
# compat where it was assumed to be an integer, as well as supporting string.
|
120
|
+
def last=(value)
|
121
|
+
@last = value.to_i
|
122
|
+
@last_str = value.to_s
|
123
|
+
end
|
124
|
+
|
89
125
|
def encode_conditions
|
90
126
|
encoded_token = @prefix.to_s.dup
|
91
127
|
encoded_token << ".s(#{set})" if set
|
92
128
|
encoded_token << ".f(#{self.from.utc.xmlschema})" if self.from
|
93
129
|
encoded_token << ".u(#{self.until.utc.xmlschema})" if self.until
|
94
|
-
encoded_token << ":#{
|
130
|
+
encoded_token << ":#{last_str}"
|
95
131
|
end
|
96
132
|
|
97
133
|
def hash_of_attributes
|
@@ -35,6 +35,13 @@ class SimpleResumptionProvider < OAI::Provider::Base
|
|
35
35
|
source_model ActiveRecordWrapper.new(DCField, :limit => 25)
|
36
36
|
end
|
37
37
|
|
38
|
+
class SimpleResumptionProviderWithNonIntegerID < OAI::Provider::Base
|
39
|
+
repository_name 'ActiveRecord Resumption Provider With Non-Integer ID'
|
40
|
+
repository_url 'http://localhost'
|
41
|
+
record_prefix 'oai:test'
|
42
|
+
source_model ActiveRecordWrapper.new(DCField, :limit => 25, identifier_field: "source")
|
43
|
+
end
|
44
|
+
|
38
45
|
class CachingResumptionProvider < OAI::Provider::Base
|
39
46
|
repository_name 'ActiveRecord Caching Resumption Provider'
|
40
47
|
repository_url 'http://localhost'
|
@@ -8,19 +8,38 @@ class SimpleResumptionProviderTest < TransactionalTestCase
|
|
8
8
|
assert_not_nil doc.elements["/OAI-PMH/ListRecords/resumptionToken"]
|
9
9
|
assert_equal 26, doc.elements["/OAI-PMH/ListRecords"].to_a.size
|
10
10
|
token = doc.elements["/OAI-PMH/ListRecords/resumptionToken"].text
|
11
|
+
|
11
12
|
doc = Document.new(@provider.list_records(:resumption_token => token))
|
12
13
|
assert_not_nil doc.elements["/OAI-PMH/ListRecords/resumptionToken"]
|
13
14
|
token = doc.elements["/OAI-PMH/ListRecords/resumptionToken"].text
|
14
15
|
assert_equal 26, doc.elements["/OAI-PMH/ListRecords"].to_a.size
|
16
|
+
|
15
17
|
doc = Document.new(@provider.list_records(:resumption_token => token))
|
16
18
|
assert_not_nil doc.elements["/OAI-PMH/ListRecords/resumptionToken"]
|
17
19
|
token = doc.elements["/OAI-PMH/ListRecords/resumptionToken"].text
|
18
20
|
assert_equal 26, doc.elements["/OAI-PMH/ListRecords"].to_a.size
|
21
|
+
|
19
22
|
doc = Document.new(@provider.list_records(:resumption_token => token))
|
20
23
|
assert_nil doc.elements["/OAI-PMH/ListRecords/resumptionToken"]
|
21
24
|
assert_equal 25, doc.elements["/OAI-PMH/ListRecords"].to_a.size
|
22
25
|
end
|
23
26
|
|
27
|
+
def test_non_integer_identifiers_resumption
|
28
|
+
@provider = SimpleResumptionProviderWithNonIntegerID.new
|
29
|
+
|
30
|
+
doc = Document.new(@provider.list_records(:metadata_prefix => 'oai_dc'))
|
31
|
+
assert_not_nil doc.elements["/OAI-PMH/ListRecords/resumptionToken"]
|
32
|
+
assert_equal 26, doc.elements["/OAI-PMH/ListRecords"].to_a.size
|
33
|
+
token = doc.elements["/OAI-PMH/ListRecords/resumptionToken"].text
|
34
|
+
|
35
|
+
next_doc = Document.new(@provider.list_records(:resumption_token => token))
|
36
|
+
assert_not_nil next_doc.elements["/OAI-PMH/ListRecords/resumptionToken"]
|
37
|
+
next_token = next_doc.elements["/OAI-PMH/ListRecords/resumptionToken"].text
|
38
|
+
assert_equal 26, next_doc.elements["/OAI-PMH/ListRecords"].to_a.size
|
39
|
+
|
40
|
+
assert_not_equal token, next_token
|
41
|
+
end
|
42
|
+
|
24
43
|
def test_from_and_until
|
25
44
|
first_id = DCField.order("id asc").first.id
|
26
45
|
DCField.where("id < #{first_id + 25}").update_all(updated_at: Time.parse("September 15 2005"))
|
@@ -43,4 +43,10 @@ class ResumptionTokenTest < Test::Unit::TestCase
|
|
43
43
|
assert_equal "#{@token.to_s}:#{@token.last}", doc.elements['/resumptionToken'].text
|
44
44
|
end
|
45
45
|
|
46
|
+
def test_resumption_token_id_does_not_need_to_be_numeric
|
47
|
+
serialized = "oai_dc.s(A).f(2005-01-01T17:00:00Z).u(2005-01-31T17:00:00Z):FA129C"
|
48
|
+
|
49
|
+
token = ResumptionToken.parse(serialized)
|
50
|
+
assert_equal serialized, token.send(:encode_conditions)
|
51
|
+
end
|
46
52
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: oai
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ed Summers
|
8
8
|
autorequire: oai
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-01-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: builder
|
@@ -195,8 +195,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
195
195
|
- !ruby/object:Gem::Version
|
196
196
|
version: '0'
|
197
197
|
requirements: []
|
198
|
-
|
199
|
-
rubygems_version: 2.7.6
|
198
|
+
rubygems_version: 3.0.3
|
200
199
|
signing_key:
|
201
200
|
specification_version: 4
|
202
201
|
summary: A ruby library for working with the Open Archive Initiative Protocol for
|