oai 1.0.1 → 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +20 -5
  3. data/Rakefile +7 -0
  4. data/bin/oai +0 -2
  5. data/examples/models/file_model.rb +2 -2
  6. data/lib/oai/client/response.rb +8 -8
  7. data/lib/oai/client.rb +34 -10
  8. data/lib/oai/exception.rb +46 -38
  9. data/lib/oai/harvester/config.rb +1 -1
  10. data/lib/oai/harvester/harvest.rb +37 -25
  11. data/lib/oai/harvester/logging.rb +3 -5
  12. data/lib/oai/harvester.rb +4 -1
  13. data/lib/oai/provider/model/activerecord_caching_wrapper.rb +5 -8
  14. data/lib/oai/provider/model/activerecord_wrapper.rb +38 -22
  15. data/lib/oai/provider/model.rb +1 -1
  16. data/lib/oai/provider/response/list_records.rb +12 -0
  17. data/lib/oai/provider/response/record_response.rb +20 -2
  18. data/lib/oai/provider/response.rb +16 -6
  19. data/lib/oai/provider/resumption_token.rb +18 -5
  20. data/lib/oai/provider.rb +129 -7
  21. data/test/activerecord_provider/database/0001_oaipmh_tables.rb +7 -1
  22. data/test/activerecord_provider/helpers/providers.rb +3 -1
  23. data/test/activerecord_provider/helpers/transactional_test_case.rb +2 -1
  24. data/test/activerecord_provider/models/dc_field.rb +8 -0
  25. data/test/activerecord_provider/models/dc_lang.rb +3 -0
  26. data/test/activerecord_provider/models/exclusive_set_dc_field.rb +6 -0
  27. data/test/activerecord_provider/tc_activerecord_wrapper.rb +63 -0
  28. data/test/activerecord_provider/tc_ar_provider.rb +54 -26
  29. data/test/activerecord_provider/tc_ar_sets_provider.rb +10 -9
  30. data/test/activerecord_provider/tc_caching_paging_provider.rb +9 -7
  31. data/test/activerecord_provider/tc_simple_paging_provider.rb +9 -7
  32. data/test/client/tc_exception.rb +1 -1
  33. data/test/client/tc_get_record.rb +1 -1
  34. data/test/client/tc_http_client.rb +2 -2
  35. data/test/client/tc_libxml.rb +1 -1
  36. data/test/client/tc_utf8_escaping.rb +8 -1
  37. data/test/harvester/tc_harvest.rb +42 -0
  38. data/test/harvester/test_helper_harvester.rb +6 -0
  39. data/test/provider/models.rb +3 -3
  40. data/test/provider/tc_functional_tokens.rb +17 -11
  41. data/test/provider/tc_instance_provider.rb +41 -0
  42. data/test/provider/tc_provider.rb +26 -0
  43. data/test/provider/tc_simple_provider.rb +16 -0
  44. data/test/provider/test_helper_provider.rb +17 -0
  45. metadata +27 -15
@@ -29,7 +29,7 @@ module OAI::Provider
29
29
  # see the ResumptionToken class for more details.
30
30
  #
31
31
  class Model
32
- attr_reader :timestamp_field, :identifier_field
32
+ attr_reader :timestamp_field, :identifier_field, :limit
33
33
 
34
34
  def initialize(limit = nil, timestamp_field = 'updated_at', identifier_field = 'id')
35
35
  @limit = limit
@@ -2,6 +2,18 @@ module OAI::Provider::Response
2
2
 
3
3
  class ListRecords < RecordResponse
4
4
  required_parameters :metadata_prefix
5
+
6
+ def valid?
7
+ super && matching_granularity?
8
+ end
9
+
10
+ def matching_granularity?
11
+ if options[:from].nil? == false && options[:until].nil? == false && options[:from].class.name != options[:until].class.name
12
+ raise OAI::ArgumentException.new, "The 'from' and 'until' options specified must have the same granularity"
13
+ else
14
+ true
15
+ end
16
+ end
5
17
 
6
18
  def to_xml
7
19
  result = provider.model.find(:all, options)
@@ -3,8 +3,26 @@ module OAI::Provider::Response
3
3
  def self.inherited(klass)
4
4
  klass.valid_parameters :metadata_prefix, :from, :until, :set
5
5
  klass.default_parameters :metadata_prefix => "oai_dc",
6
- :from => Proc.new {|x| Time.parse(x.provider.model.earliest.to_s) }, #-- OAI 2.0 hack - UTC
7
- :until => Proc.new {|x| Time.parse(x.provider.model.latest.to_s) } #-- OAI 2.0 hack - UTC
6
+ :from => method(:default_from).to_proc,
7
+ :until => method(:default_until).to_proc
8
+ end
9
+
10
+ def self.default_from(response)
11
+ value = Time.parse(response.provider.model.earliest.to_s).utc
12
+ if response.options[:until]
13
+ u = parse_date(response.options[:until])
14
+ value = value.to_date if u.is_a? Date
15
+ end
16
+ value
17
+ end
18
+
19
+ def self.default_until(response)
20
+ value = Time.parse(response.provider.model.latest.to_s).utc
21
+ if response.options[:from]
22
+ f = parse_date(response.options[:from])
23
+ value = value.to_date if f.is_a? Date
24
+ end
25
+ value
8
26
  end
9
27
 
10
28
  # emit record header
@@ -40,6 +40,21 @@ module OAI
40
40
  yield @builder
41
41
  end
42
42
  end
43
+
44
+ protected
45
+
46
+ def self.parse_date(value)
47
+ return value if value.respond_to?(:strftime)
48
+
49
+ if value[-1] == "Z"
50
+ Time.strptime(value, "%Y-%m-%dT%H:%M:%S%Z").utc
51
+ else
52
+ Date.strptime(value, "%Y-%m-%d")
53
+ end
54
+ rescue ArgumentError => e
55
+ raise OAI::ArgumentException.new, "unparsable date: '#{value}'"
56
+ end
57
+
43
58
  private
44
59
 
45
60
  def header
@@ -89,12 +104,7 @@ module OAI
89
104
  end
90
105
 
91
106
  def parse_date(value)
92
- return value if value.respond_to?(:strftime)
93
-
94
- Date.parse(value) # This will raise an exception for badly formatted dates
95
- Time.parse(value).utc # -- UTC Bug fix hack 8/08 not in core
96
- rescue
97
- raise OAI::ArgumentException.new, "unparsable date: '#{value}'"
107
+ self.class.parse_date(value)
98
108
  end
99
109
 
100
110
  def internalize(hash = {})
@@ -1,5 +1,4 @@
1
1
  require 'time'
2
- require 'enumerator'
3
2
  require File.dirname(__FILE__) + "/partial_result"
4
3
 
5
4
  module OAI::Provider
@@ -37,7 +36,7 @@ module OAI::Provider
37
36
  attr_reader :prefix, :set, :from, :until, :last, :last_str, :expiration, :total
38
37
 
39
38
  # parses a token string and returns a ResumptionToken
40
- def self.parse(token_string)
39
+ def self.parse(token_string, expiration = nil, total = nil)
41
40
  begin
42
41
  options = {}
43
42
  matches = /(.+):([^ :]+)$/.match(token_string)
@@ -55,7 +54,7 @@ module OAI::Provider
55
54
  options[:until] = Time.parse(part.sub(/^u\(/, '').sub(/\)$/, '')).localtime
56
55
  end
57
56
  end
58
- self.new(options)
57
+ self.new(options, expiration, total)
59
58
  rescue => err
60
59
  raise OAI::ResumptionTokenException.new
61
60
  end
@@ -123,10 +122,24 @@ module OAI::Provider
123
122
  end
124
123
 
125
124
  def encode_conditions
125
+ return "" if last_str.nil? || last_str.to_s.strip.eql?("")
126
+
126
127
  encoded_token = @prefix.to_s.dup
127
128
  encoded_token << ".s(#{set})" if set
128
- encoded_token << ".f(#{self.from.utc.xmlschema})" if self.from
129
- encoded_token << ".u(#{self.until.utc.xmlschema})" if self.until
129
+ if self.from
130
+ if self.from.respond_to?(:utc)
131
+ encoded_token << ".f(#{self.from.utc.xmlschema})"
132
+ else
133
+ encoded_token << ".f(#{self.from.xmlschema})"
134
+ end
135
+ end
136
+ if self.until
137
+ if self.until.respond_to?(:utc)
138
+ encoded_token << ".u(#{self.until.utc.xmlschema})"
139
+ else
140
+ encoded_token << ".u(#{self.until.xmlschema})"
141
+ end
142
+ end
130
143
  encoded_token << ":#{last_str}"
131
144
  end
132
145
 
data/lib/oai/provider.rb CHANGED
@@ -139,6 +139,35 @@ end
139
139
  #
140
140
  # Special thanks to Jose Hales-Garcia for this solution.
141
141
  #
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
+ #
142
171
  # ## Supporting custom metadata formats
143
172
  #
144
173
  # See {OAI::MetadataFormat} for details.
@@ -292,39 +321,132 @@ module OAI::Provider
292
321
 
293
322
  Base.register_format(OAI::Provider::Metadata::DublinCore.instance)
294
323
 
324
+ PROVIDER_CONTEXTS = {
325
+ :class_based => :class_based,
326
+ :instance_based => :instance_based
327
+ }
328
+
329
+ def initialize(options = {})
330
+ provider_context = options.fetch(:provider_context) { :class_based }
331
+ @provider_context = PROVIDER_CONTEXTS.fetch(provider_context)
332
+ end
333
+
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.)
361
+ def provider_context
362
+ if @provider_context == :instance_based
363
+ self
364
+ else
365
+ self.class
366
+ end
367
+ end
368
+
369
+ def format_supported?(*args)
370
+ self.class.format_supported?(*args)
371
+ end
372
+
373
+ def format(*args)
374
+ self.class.format(*args)
375
+ end
376
+
377
+ def formats
378
+ self.class.formats
379
+ end
380
+
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
+
295
417
  # Equivalent to '&verb=Identify', returns information about the repository
296
418
  def identify(options = {})
297
- Response::Identify.new(self.class, options).to_xml
419
+ Response::Identify.new(provider_context, options).to_xml
298
420
  end
299
421
 
300
422
  # Equivalent to '&verb=ListSets', returns a list of sets that are supported
301
423
  # by the repository or an error if sets are not supported.
302
424
  def list_sets(options = {})
303
- Response::ListSets.new(self.class, options).to_xml
425
+ Response::ListSets.new(provider_context, options).to_xml
304
426
  end
305
427
 
306
428
  # Equivalent to '&verb=ListMetadataFormats', returns a list of metadata formats
307
429
  # supported by the repository.
308
430
  def list_metadata_formats(options = {})
309
- Response::ListMetadataFormats.new(self.class, options).to_xml
431
+ Response::ListMetadataFormats.new(provider_context, options).to_xml
310
432
  end
311
433
 
312
434
  # Equivalent to '&verb=ListIdentifiers', returns a list of record headers that
313
435
  # meet the supplied criteria.
314
436
  def list_identifiers(options = {})
315
- Response::ListIdentifiers.new(self.class, options).to_xml
437
+ Response::ListIdentifiers.new(provider_context, options).to_xml
316
438
  end
317
439
 
318
440
  # Equivalent to '&verb=ListRecords', returns a list of records that meet the
319
441
  # supplied criteria.
320
442
  def list_records(options = {})
321
- Response::ListRecords.new(self.class, options).to_xml
443
+ Response::ListRecords.new(provider_context, options).to_xml
322
444
  end
323
445
 
324
446
  # Equivalent to '&verb=GetRecord', returns a record matching the required
325
447
  # :identifier option
326
448
  def get_record(options = {})
327
- Response::GetRecord.new(self.class, options).to_xml
449
+ Response::GetRecord.new(provider_context, options).to_xml
328
450
  end
329
451
 
330
452
  # xml_response = process_verb('ListRecords', :from => 'October 1, 2005',
@@ -336,7 +458,7 @@ module OAI::Provider
336
458
  begin
337
459
 
338
460
  # Allow the request to pass in a url
339
- self.class.url = params['url'] ? params.delete('url') : self.class.url
461
+ provider_context.url = params['url'] ? params.delete('url') : self.url
340
462
 
341
463
  verb = params.delete('verb') || params.delete(:verb)
342
464
 
@@ -10,6 +10,12 @@ class OaipmhTables < ActiveRecord::Migration[5.2]
10
10
  t.column :oai_token_id, :integer, :null => false
11
11
  end
12
12
 
13
+ create_table :dc_langs do |t|
14
+ t.column :name, :string
15
+ t.column :updated_at, :datetime
16
+ t.column :created_at, :datetime
17
+ end
18
+
13
19
  dc_fields = proc do |t|
14
20
  t.column :title, :string
15
21
  t.column :creator, :string
@@ -21,7 +27,7 @@ class OaipmhTables < ActiveRecord::Migration[5.2]
21
27
  t.column :type, :string
22
28
  t.column :format, :string
23
29
  t.column :source, :string
24
- t.column :language, :string
30
+ t.column :dc_lang_id, :integer
25
31
  t.column :relation, :string
26
32
  t.column :coverage, :string
27
33
  t.column :rights, :string
@@ -56,11 +56,13 @@ class ARLoader
56
56
  File.join(File.dirname(__FILE__), '..', 'fixtures', 'dc.yml')
57
57
  )
58
58
  fixtures.keys.sort.each do |key|
59
- DCField.create(fixtures[key])
59
+ lang = DCLang.create(name: fixtures[key].delete('language'))
60
+ DCField.create(fixtures[key].merge(dc_lang: lang))
60
61
  end
61
62
  end
62
63
 
63
64
  def self.unload
64
65
  DCField.delete_all
66
+ DCLang.delete_all
65
67
  end
66
68
  end
@@ -19,7 +19,8 @@ class TransactionalTestCase < Test::Unit::TestCase
19
19
  )
20
20
  disable_logging do
21
21
  fixtures.keys.sort.each do |key|
22
- DCField.create(fixtures[key])
22
+ lang = DCLang.create(name: fixtures[key].delete('language'))
23
+ DCField.create(fixtures[key].merge(dc_lang: lang))
23
24
  end
24
25
  end
25
26
  end
@@ -4,4 +4,12 @@ class DCField < ActiveRecord::Base
4
4
  :join_table => "dc_fields_dc_sets",
5
5
  :foreign_key => "dc_field_id",
6
6
  :class_name => "DCSet"
7
+
8
+ belongs_to :dc_lang, class_name: "DCLang", optional: true
9
+
10
+ default_scope -> { left_outer_joins(:dc_lang) }
11
+
12
+ def language
13
+ dc_lang&.name
14
+ end
7
15
  end
@@ -0,0 +1,3 @@
1
+ class DCLang < ActiveRecord::Base
2
+ has_many :dc_fields
3
+ end
@@ -8,4 +8,10 @@ class ExclusiveSetDCField < ActiveRecord::Base
8
8
  end
9
9
  end
10
10
 
11
+ belongs_to :dc_lang, class_name: "DCLang", optional: true
12
+
13
+ def language
14
+ dc_lang&.name
15
+ end
16
+
11
17
  end
@@ -0,0 +1,63 @@
1
+ require 'test_helper_ar_provider'
2
+
3
+ class ActiveRecordWrapperTest < TransactionalTestCase
4
+ def test_sql_conditions_from_date
5
+ input = "2005-12-25"
6
+ expected = input.dup
7
+ sql_template, sql_opts = sql_conditions(from: input)
8
+ assert_equal "dc_fields.updated_at >= :from", sql_template
9
+ assert_equal expected, sql_opts[:from]
10
+ sql_template, sql_opts = sql_conditions(from: Date.strptime(input, "%Y-%m-%d"))
11
+ assert_equal "dc_fields.updated_at >= :from", sql_template
12
+ assert_equal expected, sql_opts[:from]
13
+ end
14
+
15
+ def test_sql_conditions_from_time
16
+ input = "2005-12-25T00:00:00Z"
17
+ expected = "2005-12-25 00:00:00"
18
+ sql_template, sql_opts = sql_conditions(from: input)
19
+ assert_equal "dc_fields.updated_at >= :from", sql_template
20
+ assert_equal expected, sql_opts[:from]
21
+ sql_template, sql_opts = sql_conditions(from: Time.strptime(input, "%Y-%m-%dT%H:%M:%S%Z"))
22
+ assert_equal "dc_fields.updated_at >= :from", sql_template
23
+ assert_equal expected, sql_opts[:from]
24
+ end
25
+
26
+ def test_sql_conditions_until_date
27
+ input = "2005-12-25"
28
+ expected = "2005-12-26"
29
+ sql_template, sql_opts = sql_conditions(until: input)
30
+ assert_equal "dc_fields.updated_at < :until", sql_template
31
+ assert_equal expected, sql_opts[:until]
32
+ sql_template, sql_opts = sql_conditions(until: Date.strptime(input, "%Y-%m-%d"))
33
+ assert_equal "dc_fields.updated_at < :until", sql_template
34
+ assert_equal expected, sql_opts[:until]
35
+ end
36
+
37
+ def test_sql_conditions_until_time
38
+ input = "2005-12-25T00:00:00Z"
39
+ expected = "2005-12-25 00:00:01"
40
+ sql_template, sql_opts = sql_conditions(until: input)
41
+ assert_equal "dc_fields.updated_at < :until", sql_template
42
+ assert_equal expected, sql_opts[:until]
43
+ sql_template, sql_opts = sql_conditions(until: Time.strptime(input, "%Y-%m-%dT%H:%M:%S%Z"))
44
+ assert_equal "dc_fields.updated_at < :until", sql_template
45
+ assert_equal expected, sql_opts[:until]
46
+ end
47
+
48
+ def test_sql_conditions_both
49
+ input = "2005-12-25"
50
+ sql_template, sql_opts = sql_conditions(from: input, until: input)
51
+ assert_equal "dc_fields.updated_at >= :from AND dc_fields.updated_at < :until", sql_template
52
+ end
53
+
54
+ def setup
55
+ @wrapper = OAI::Provider::ActiveRecordWrapper.new(DCField)
56
+ end
57
+
58
+ def sql_conditions(opts)
59
+ @wrapper.send :sql_conditions, opts
60
+ end
61
+ end
62
+
63
+
@@ -81,30 +81,30 @@ class ActiveRecordProviderTest < TransactionalTestCase
81
81
 
82
82
  def test_from
83
83
  first_id = DCField.order("id asc").first.id
84
- DCField.where("id < #{first_id + 90}").update_all(updated_at: Time.parse("January 1 2005"))
84
+ DCField.where("dc_fields.id < #{first_id + 90}").update_all(updated_at: Time.parse("January 1 2005"))
85
85
 
86
- DCField.where("id < #{first_id + 10}").update_all(updated_at: Time.parse("June 1 2005"))
86
+ DCField.where("dc_fields.id < #{first_id + 10}").update_all(updated_at: Time.parse("June 1 2005"))
87
87
 
88
88
 
89
- from_param = Time.parse("January 1 2006")
89
+ from_param = Time.parse("January 1 2006").getutc.iso8601
90
90
 
91
91
  doc = REXML::Document.new(
92
92
  @provider.list_records(
93
93
  :metadata_prefix => 'oai_dc', :from => from_param)
94
94
  )
95
- assert_equal DCField.where(["updated_at >= ?", from_param]).size,
95
+ assert_equal DCField.where(["dc_fields.updated_at >= ?", from_param]).size,
96
96
  doc.elements['OAI-PMH/ListRecords'].size
97
97
 
98
98
  doc = REXML::Document.new(
99
99
  @provider.list_records(
100
- :metadata_prefix => 'oai_dc', :from => Time.parse("May 30 2005"))
100
+ :metadata_prefix => 'oai_dc', :from => Time.parse("May 30 2005").getutc.iso8601)
101
101
  )
102
102
  assert_equal 20, doc.elements['OAI-PMH/ListRecords'].to_a.size
103
103
  end
104
104
 
105
105
  def test_until
106
- first_id = DCField.order("id asc").first.id
107
- DCField.where("id < #{first_id + 10}").update_all(updated_at: Time.parse("June 1 2005"))
106
+ first_id = DCField.order(id: :asc).first.id
107
+ DCField.where("dc_fields.id < ?", first_id + 10).update_all(updated_at: Time.parse("June 1 2005"))
108
108
 
109
109
  doc = REXML::Document.new(
110
110
  @provider.list_records(
@@ -114,19 +114,46 @@ class ActiveRecordProviderTest < TransactionalTestCase
114
114
  end
115
115
 
116
116
  def test_from_and_until
117
- first_id = DCField.order("id asc").first.id
117
+ first_id = DCField.order(id: :asc).first.id
118
118
  DCField.update_all(updated_at: Time.parse("June 1 2005"))
119
- DCField.where("id < #{first_id + 50}").update_all(updated_at: Time.parse("June 15 2005"))
120
- DCField.where("id < #{first_id + 10}").update_all(updated_at: Time.parse("June 30 2005"))
119
+ DCField.where("dc_fields.id < ?", first_id + 50).update_all(updated_at: Time.parse("June 15 2005"))
120
+ DCField.where("dc_fields.id < ?", first_id + 10).update_all(updated_at: Time.parse("June 30 2005"))
121
121
 
122
122
  doc = REXML::Document.new(
123
123
  @provider.list_records(
124
124
  :metadata_prefix => 'oai_dc',
125
- :from => Time.parse("June 3 2005"),
126
- :until => Time.parse("June 16 2005"))
125
+ :from => Time.parse("June 3 2005").getutc.iso8601,
126
+ :until => Time.parse("June 16 2005").getutc.iso8601)
127
127
  )
128
128
  assert_equal 40, doc.elements['OAI-PMH/ListRecords'].to_a.size
129
129
  end
130
+
131
+ def test_bad_until_raises_exception
132
+ DCField.order(id: :asc).limit(10).update_all(updated_at: 1.year.ago)
133
+ DCField.order(id: :desc).limit(10).update_all(updated_at: 1.year.from_now)
134
+ badTimes = [
135
+ 'junk',
136
+ 'February 92nd, 2015']
137
+ badTimes.each do |time|
138
+ assert_raise(OAI::ArgumentException) do
139
+ @provider.list_records(:metadata_prefix => 'oai_dc', :until => time)
140
+ end
141
+ end
142
+ end
143
+
144
+ def test_bad_from_raises_exception
145
+ DCField.order(id: :asc).limit(10).update_all(updated_at: 1.year.ago)
146
+ DCField.order(id: :desc).limit(10).update_all(updated_at: 1.year.from_now)
147
+
148
+ badTimes = [
149
+ 'junk',
150
+ 'February 92nd, 2015']
151
+ badTimes.each do |time|
152
+ assert_raise(OAI::ArgumentException) do
153
+ @provider.list_records(:metadata_prefix => 'oai_dc', :from => time)
154
+ end
155
+ end
156
+ end
130
157
 
131
158
  def test_handles_empty_collections
132
159
  DCField.delete_all
@@ -142,6 +169,21 @@ class ActiveRecordProviderTest < TransactionalTestCase
142
169
  REXML::Document.new(@provider.list_records(:metadata_prefix => 'oai_dc'))
143
170
  end
144
171
  end
172
+
173
+ def test_bad_id_raises_exception
174
+ badIdentifiers = [
175
+ 'invalid"id',
176
+ 'oai:test/5000',
177
+ 'oai:test/-1',
178
+ 'oai:test/one',
179
+ 'oai:test/\\$1\1!']
180
+ badIdentifiers.each do |id|
181
+ assert_raise(OAI::IdException) do
182
+ @provider.get_record(:identifier => id, :metadata_prefix => 'oai_dc')
183
+ end
184
+ end
185
+ end
186
+
145
187
 
146
188
  def setup
147
189
  @provider = ARProvider.new
@@ -149,18 +191,4 @@ class ActiveRecordProviderTest < TransactionalTestCase
149
191
 
150
192
  end
151
193
 
152
- class ActiveRecordProviderTimezoneTest < ActiveRecordProviderTest
153
-
154
- def setup
155
- require 'active_record'
156
- ActiveRecord::Base.default_timezone = :utc
157
- super
158
- end
159
194
 
160
- def teardown
161
- require 'active_record'
162
- ActiveRecord::Base.default_timezone = :local
163
- super
164
- end
165
-
166
- end
@@ -51,22 +51,22 @@ class ActiveRecordSetProviderTest < TransactionalTestCase
51
51
  set_ab = DCSet.create(:name => "Set A:B", :spec => "A:B")
52
52
 
53
53
  next_id = 0
54
- DCField.limit(10).order("id asc").each do |record|
54
+ DCField.limit(10).order(id: :asc).each do |record|
55
55
  set_a.dc_fields << record
56
56
  next_id = record.id
57
57
  end
58
58
 
59
- DCField.where("id > #{next_id}").limit(10).order("id asc").each do |record|
59
+ DCField.where("dc_fields.id > ?", next_id).limit(10).order(id: :asc).each do |record|
60
60
  set_b.dc_fields << record
61
61
  next_id = record.id
62
62
  end
63
63
 
64
- DCField.where("id > #{next_id}").limit(10).order("id asc").each do |record|
64
+ DCField.where("dc_fields.id > ?", next_id).limit(10).order(id: :asc).each do |record|
65
65
  set_ab.dc_fields << record
66
66
  next_id = record.id
67
67
  end
68
68
 
69
- DCField.where("id > #{next_id}").limit(10).order("id asc").each do |record|
69
+ DCField.where("dc_fields.id > ?", next_id).limit(10).order(id: :asc).each do |record|
70
70
  set_a.dc_fields << record
71
71
  set_c.dc_fields << record
72
72
  next_id = record.id
@@ -117,25 +117,25 @@ class ActiveRecordExclusiveSetsProviderTest < TransactionalTestCase
117
117
  def define_sets
118
118
  next_id = 0
119
119
 
120
- ExclusiveSetDCField.limit(10).order("id asc").each do |record|
120
+ ExclusiveSetDCField.limit(10).order(id: :asc).each do |record|
121
121
  record.set = "A"
122
122
  record.save!
123
123
  next_id = record.id
124
124
  end
125
125
 
126
- ExclusiveSetDCField.where("id > #{next_id}").limit(10).order("id asc").each do |record|
126
+ ExclusiveSetDCField.where("id > ?", next_id).limit(10).order(id: :asc).each do |record|
127
127
  record.set = "B"
128
128
  record.save!
129
129
  next_id = record.id
130
130
  end
131
131
 
132
- ExclusiveSetDCField.where("id > #{next_id}").limit(10).order("id asc").each do |record|
132
+ ExclusiveSetDCField.where("id > ?", next_id).limit(10).order(id: :asc).each do |record|
133
133
  record.set = "A:B"
134
134
  record.save!
135
135
  next_id = record.id
136
136
  end
137
137
 
138
- ExclusiveSetDCField.where("id > #{next_id}").limit(10).order("id asc").each do |record|
138
+ ExclusiveSetDCField.where("id > ?", next_id).limit(10).order(id: :asc).each do |record|
139
139
  record.set = "A"
140
140
  record.save!
141
141
  next_id = record.id
@@ -150,7 +150,8 @@ class ActiveRecordExclusiveSetsProviderTest < TransactionalTestCase
150
150
  )
151
151
  disable_logging do
152
152
  fixtures.keys.sort.each do |key|
153
- ExclusiveSetDCField.create(fixtures[key])
153
+ lang = DCLang.create(name: fixtures[key].delete('language'))
154
+ ExclusiveSetDCField.create(fixtures[key].merge(dc_lang: lang))
154
155
  end
155
156
  end
156
157
  end