oai_repository 0.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.
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2012 Intersect Australia Ltd.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,5 @@
1
+ = OAI Repository
2
+
3
+ A Rails (3.1+) engine that allows you to expose your models through an OAI-PHM Data Provider interface.
4
+
5
+ See http://www.oaforum.org/tutorial/ and http://www.openarchives.org/OAI/openarchivesprotocol.html#Repository
data/Rakefile ADDED
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+ begin
8
+ require 'rdoc/task'
9
+ rescue LoadError
10
+ require 'rdoc/rdoc'
11
+ require 'rake/rdoctask'
12
+ RDoc::Task = Rake::RDocTask
13
+ end
14
+
15
+ RDoc::Task.new(:rdoc) do |rdoc|
16
+ rdoc.rdoc_dir = 'rdoc'
17
+ rdoc.title = 'OaiRepository'
18
+ rdoc.options << '--line-numbers'
19
+ rdoc.rdoc_files.include('README.rdoc')
20
+ rdoc.rdoc_files.include('lib/**/*.rb')
21
+ end
22
+
23
+ APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
24
+ load 'rails/tasks/engine.rake'
25
+
26
+
27
+
28
+ Bundler::GemHelper.install_tasks
29
+
@@ -0,0 +1,15 @@
1
+ // This is a manifest file that'll be compiled into application.js, which will include all the files
2
+ // listed below.
3
+ //
4
+ // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5
+ // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
6
+ //
7
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8
+ // the compiled file.
9
+ //
10
+ // WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
11
+ // GO AFTER THE REQUIRES BELOW.
12
+ //
13
+ //= require jquery
14
+ //= require jquery_ujs
15
+ //= require_tree .
@@ -0,0 +1,2 @@
1
+ // Place all the behaviors and hooks related to the matching controller here.
2
+ // All this logic will automatically be available in application.js.
@@ -0,0 +1,13 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the top of the
9
+ * compiled file, but it's generally better to create a new file per style scope.
10
+ *
11
+ *= require_self
12
+ *= require_tree .
13
+ */
@@ -0,0 +1,4 @@
1
+ /*
2
+ Place all the styles related to the matching controller here.
3
+ They will automatically be included in application.css.
4
+ */
@@ -0,0 +1,4 @@
1
+ module OaiRepository
2
+ class ApplicationController < ActionController::Base
3
+ end
4
+ end
@@ -0,0 +1,18 @@
1
+ module OaiRepository
2
+ class ServicesController < ApplicationController
3
+ require 'oai_provider'
4
+
5
+ def show
6
+
7
+ options = params.delete_if { |k,v| %w{controller action}.include?(k) }
8
+ response = get_provider.process_request(options)
9
+ render :xml => response
10
+
11
+ end
12
+
13
+ def get_provider
14
+ @provider ||= OAIProvider::provider.new
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,4 @@
1
+ module OaiRepository
2
+ module ApplicationHelper
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module OaiRepository
2
+ module ServicesHelper
3
+ end
4
+ end
@@ -0,0 +1,14 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>OaiRepository</title>
5
+ <%= stylesheet_link_tag "oai_repository/application", :media => "all" %>
6
+ <%= javascript_include_tag "oai_repository/application" %>
7
+ <%= csrf_meta_tags %>
8
+ </head>
9
+ <body>
10
+
11
+ <%= yield %>
12
+
13
+ </body>
14
+ </html>
@@ -0,0 +1,2 @@
1
+ <h1>Services#show</h1>
2
+ <p>Find me in app/views/oai_repository/services/show.html.erb</p>
data/config/routes.rb ADDED
@@ -0,0 +1,7 @@
1
+ OaiRepository::Engine.routes.draw do
2
+
3
+ get "services/show"
4
+
5
+ root :to => "services#show"
6
+
7
+ end
@@ -0,0 +1,156 @@
1
+ include OAI::Provider
2
+ class ARWrapperModel < OAI::Provider::Model
3
+
4
+ def initialize(options={})
5
+ @timestamp_field = options.delete(:timestamp_field) || 'updated_at'
6
+ @limit = options.delete(:limit)
7
+ @sets_map = options.delete(:sets)
8
+ @oai_dc_mapping = {}
9
+
10
+ unless options.empty?
11
+ raise ArgumentError.new(
12
+ "Unsupported options [#{options.keys.join(', ')}]"
13
+ )
14
+ end
15
+
16
+ end
17
+
18
+ def map_oai_dc
19
+ if @oai_dc_mapping.empty?
20
+ dc = OAI::Provider::Metadata::DublinCore.instance
21
+ dc.fields.map do |field|
22
+ @oai_dc_mapping[field.to_sym] = "oai_dc_#{field}".to_sym
23
+ end
24
+ end
25
+ @oai_dc_mapping
26
+ end
27
+
28
+ def sets
29
+ @sets_map.map {|set_obj|
30
+ OAI::Set.new(
31
+ {
32
+ name: set_obj[:name],
33
+ spec: set_obj[:spec],
34
+ description: set_obj[:description]
35
+ }
36
+ )
37
+ }
38
+ end
39
+
40
+ def earliest
41
+ record_end(:asc)
42
+ end
43
+
44
+ def latest
45
+ record_end(:desc)
46
+ end
47
+
48
+ # selector can be id or :all
49
+ def find(selector, options={})
50
+ token = nil
51
+ if options[:resumption_token]
52
+ raise OAI::ResumptionTokenException.new unless @limit
53
+ token = ResumptionToken.parse(options[:resumption_token])
54
+ end
55
+
56
+ from = token ? token.from : options[:from]
57
+ to = token ? token.until : options[:until]
58
+ last = token ? token.last : 0
59
+ prefix = token ? token.prefix : options[:metadata_prefix]
60
+ set = token ? token.set : options[:set]
61
+
62
+ conditions = sql_conditions(:from => from, :until => to)
63
+ record_rows = get_record_rows(get_models(set), conditions)
64
+
65
+ return get_specific_record(record_rows, selector) if selector != :all
66
+
67
+ if @limit and record_rows.size > @limit
68
+ get_paged_records(record_rows, :from => from, :until => to, :set => set, :last => last, :metadata_prefix => prefix)
69
+ else
70
+ get_record_objects(record_rows)
71
+ end
72
+ end
73
+
74
+ private
75
+
76
+ def get_specific_record(records, id)
77
+ # TODO: optimise somehow
78
+ # This is terribly intensive :-(
79
+ records.each do |record|
80
+ obj = Object.const_get(record["type"]).find(record["id"])
81
+ return obj if obj.oai_dc_identifier.eql?(id)
82
+ end
83
+ raise OAI::IdException.new
84
+ end
85
+
86
+ def get_paged_records(record_rows, options)
87
+
88
+ if record_rows.size < options[:last]
89
+ raise OAI::ResumptionTokenException.new
90
+ end
91
+
92
+ list = get_record_objects(record_rows[options[:last], @limit])
93
+
94
+ if list.size < @limit
95
+ list
96
+ else
97
+ options[:last] += @limit
98
+ PartialResult.new(list, ResumptionToken.new(options))
99
+ end
100
+ end
101
+
102
+ def sql_conditions(opts)
103
+ from = Time.parse(opts[:from].to_s).localtime
104
+ to = Time.parse(opts[:until].to_s).localtime.to_s
105
+ return "#{timestamp_field} >= #{ActiveRecord::Base.sanitize(from)} AND #{timestamp_field} <= #{ActiveRecord::Base.sanitize(to)}"
106
+ end
107
+
108
+ def get_models(set)
109
+ models =
110
+ if set.nil?
111
+ @sets_map.map{|s| s[:model]}
112
+ else
113
+ @sets_map.select{|s| s[:spec] == set}.map{|s| s[:model]}
114
+ end
115
+
116
+ if models.empty?
117
+ raise OAI::NoMatchException.new
118
+ end
119
+ models
120
+ end
121
+
122
+ def get_record_rows(models, conditions)
123
+ record_sql = models.map do |m|
124
+ "select id, '#{m.name}' as type, #{timestamp_field} from #{m.table_name} where #{conditions}"
125
+ end.join(" UNION ")
126
+
127
+ sorted_list_sql = "select t.id as id, t.type as type, t.updated_at as updated_at from (#{record_sql}) t order by t.updated_at desc"
128
+ records = ActiveRecord::Base.connection.execute(sorted_list_sql).to_a
129
+ end
130
+
131
+ def get_record_objects(records)
132
+ records.map do |record|
133
+ Object.const_get(record["type"]).find(record["id"])
134
+ end
135
+ end
136
+
137
+ def record_end(order)
138
+ record = nil
139
+ @sets_map.each do |s|
140
+ r = s[:model].first(:order => "#{@timestamp_field} #{order.to_s}")
141
+ next if r.nil?
142
+
143
+ if record.nil?
144
+ record = r
145
+ elsif order == :asc and r.send(@timestamp_field) < record.send(@timestamp_field)
146
+ record = r
147
+ elsif order == :desc and r.send(@timestamp_field) > record.send(@timestamp_field)
148
+ record = r
149
+ end
150
+ end
151
+ raise OAI::NoMatchException if record.nil?
152
+ record.send(@timestamp_field)
153
+ end
154
+
155
+ end
156
+
@@ -0,0 +1,12 @@
1
+ module OaiRepository
2
+ module Generators
3
+ class InstallGenerator < Rails::Generators::Base
4
+ source_root File.expand_path("../../templates", __FILE__)
5
+
6
+ desc "Creates an OAI Repository initializer."
7
+ def copy_initializer
8
+ template "oai_repository.rb", "config/initializers/oai_repository.rb"
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,45 @@
1
+ require 'rifcs_format'
2
+ OaiRepository.setup do |config|
3
+
4
+ config.repository_name = 'NAME YOUR REPOSITORY'
5
+
6
+ # The URL from which this OAI Repository is served.
7
+ # If you're deploying to different hostnames (e.g. development, QA and
8
+ # production environments, each with different hostnames), you could
9
+ # conditionally set this.
10
+ config.repository_url = 'URL OF THE REPOSITORY'
11
+
12
+ # By default the (unique) identifier of each record will be composed as
13
+ # #{record_prefix}/#{record.id}
14
+ # This is probably not want you want, especially if you have multiple record
15
+ # sets (i.e. this provider serves multiple ActiveRecord models)
16
+ #
17
+ # Most probably you'll create an oai_dc_identifier attribute or method in
18
+ # the AR models you intend to serve. That value will supplant the default.
19
+ config.record_prefix = 'http://localhost:3000/'
20
+
21
+ config.admin_email = 'change_me@example.com'
22
+
23
+ # Map the name of the set to the ActiveRecord (or other) class name that
24
+ # will provide (at a minimum) the required oai_dc attributes/methods.
25
+ # E.g.
26
+ # config.sets = [
27
+ # {
28
+ # spec: 'class:party',
29
+ # name: 'Parties',
30
+ # model: Person
31
+ # },
32
+ # {
33
+ # spec: 'class:service',
34
+ # name: 'Services',
35
+ # description: 'Things that are services',
36
+ # model: Instrument
37
+ # }
38
+ # ]
39
+ config.sets = []
40
+
41
+ config.additional_formats = [
42
+ OAI::Provider::Metadata::RIFCS
43
+ ]
44
+
45
+ end
@@ -0,0 +1,34 @@
1
+ require 'oai'
2
+ require 'patched_record_response'
3
+ require 'ar_wrapper_model'
4
+ module OAIProvider
5
+
6
+ @@provider = nil
7
+
8
+ def self.provider
9
+ @@provider ||= build_set_provider
10
+ end
11
+
12
+ private
13
+
14
+ def self.build_set_provider
15
+
16
+ provider_name = "ARProvider"
17
+ provider_class = Object.const_set(provider_name, Class.new(OAI::Provider::Base))
18
+ provider_class.repository_name OaiRepository.repository_name
19
+ provider_class.repository_url OaiRepository.repository_url
20
+ provider_class.record_prefix OaiRepository.record_prefix
21
+ provider_class.admin_email OaiRepository.admin_email
22
+ provider_class.source_model ARWrapperModel.new(
23
+ sets: OaiRepository.sets,
24
+ limit: OaiRepository.limit,
25
+ timestamp_field: OaiRepository.timestamp_field
26
+ )
27
+ OaiRepository.additional_formats.each do |format|
28
+ provider_class.register_format(format.instance)
29
+ end
30
+ provider_class
31
+
32
+ end
33
+
34
+ end
@@ -0,0 +1,5 @@
1
+ module OaiRepository
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace OaiRepository
4
+ end
5
+ end
@@ -0,0 +1,3 @@
1
+ module OaiRepository
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,33 @@
1
+ require "oai_repository/engine"
2
+
3
+ module OaiRepository
4
+
5
+ def self.setup
6
+ yield self
7
+ end
8
+
9
+ mattr_accessor :repository_name
10
+ @@repository_name = 'Unspecified'
11
+
12
+ mattr_accessor :repository_url
13
+ @@repository_url = nil
14
+
15
+ mattr_accessor :record_prefix
16
+ @@record_prefix = 'PREFIX'
17
+
18
+ mattr_accessor :admin_email
19
+ @@admin_email = 'root@localhost'
20
+
21
+ mattr_accessor :sets
22
+ @@sets = {}
23
+
24
+ mattr_accessor :additional_formats
25
+ @@formats = []
26
+ ##OAI::Provider::Metadata::RIFCS.instance
27
+
28
+ mattr_accessor :limit
29
+ @@limit = 100
30
+
31
+ mattr_accessor :timestamp_field
32
+ @@timestamp_field = 'updated_at'
33
+ end
@@ -0,0 +1,16 @@
1
+ module OAI::Provider::Response
2
+ class RecordResponse < Base
3
+
4
+ private
5
+
6
+ def identifier_for(record)
7
+ if record.respond_to?(:oai_dc_identifier)
8
+ record.oai_dc_identifier
9
+ else
10
+ "#{provider.prefix}/#{record.id}"
11
+ end
12
+ end
13
+
14
+ end
15
+
16
+ end
@@ -0,0 +1,22 @@
1
+ require 'oai'
2
+ module OAI::Provider::Metadata
3
+
4
+ class RIFCS < Format
5
+ def initialize
6
+ @prefix = 'rif'
7
+ @schema = 'http://services.ands.org.au/documentation/rifcs/1.3/schema/registryObjects.xsd'
8
+ @namespace = 'http://ands.org.au/standards/rif-cs/registryObjects'
9
+ @element_namespace = 'rif-cs'
10
+ end
11
+
12
+ def header_specification
13
+ {
14
+ 'xmlns' => "http://ands.org.au/standards/rif-cs/registryObjects" ,
15
+ 'xmlns:xsi' => "http://www.w3.org/2001/XMLSchema-instance" ,
16
+ 'xsi:schemaLocation' => "http://ands.org.au/standards/rif-cs/registryObjects http://services.ands.org.au/documentation/rifcs/1.3/schema/registryObjects.xsd"
17
+ }
18
+ end
19
+
20
+ end
21
+
22
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :oai_repository do
3
+ # # Task goes here
4
+ # end
metadata ADDED
@@ -0,0 +1,133 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: oai_repository
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Sean McCarthy
9
+ - Diego Alonso de Marcos
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2012-07-16 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rails
17
+ requirement: &77154580 !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ~>
21
+ - !ruby/object:Gem::Version
22
+ version: '3.1'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: *77154580
26
+ - !ruby/object:Gem::Dependency
27
+ name: oai
28
+ requirement: &77154060 !ruby/object:Gem::Requirement
29
+ none: false
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: *77154060
37
+ - !ruby/object:Gem::Dependency
38
+ name: sqlite3
39
+ requirement: &77153150 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ! '>='
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
45
+ type: :development
46
+ prerelease: false
47
+ version_requirements: *77153150
48
+ - !ruby/object:Gem::Dependency
49
+ name: rspec-rails
50
+ requirement: &77202670 !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ! '>='
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ type: :development
57
+ prerelease: false
58
+ version_requirements: *77202670
59
+ - !ruby/object:Gem::Dependency
60
+ name: capybara
61
+ requirement: &77200990 !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ! '>='
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ type: :development
68
+ prerelease: false
69
+ version_requirements: *77200990
70
+ description: An Engine for Rails (3.1+) that allows you to make your application an
71
+ OAI-PMH Data Provider. See http://www.openarchives.org/pmh/ and http://www.openarchives.org/OAI/openarchivesprotocol.html#Repository
72
+ email:
73
+ - sean@intersect.org.au
74
+ - diego@intersect.org.au
75
+ executables: []
76
+ extensions: []
77
+ extra_rdoc_files: []
78
+ files:
79
+ - app/controllers/oai_repository/application_controller.rb
80
+ - app/controllers/oai_repository/services_controller.rb
81
+ - app/assets/stylesheets/oai_repository/services.css
82
+ - app/assets/stylesheets/oai_repository/application.css
83
+ - app/assets/javascripts/oai_repository/application.js
84
+ - app/assets/javascripts/oai_repository/services.js
85
+ - app/helpers/oai_repository/services_helper.rb
86
+ - app/helpers/oai_repository/application_helper.rb
87
+ - app/views/oai_repository/services/show.html.erb
88
+ - app/views/layouts/oai_repository/application.html.erb
89
+ - config/routes.rb
90
+ - lib/patched_record_response.rb
91
+ - lib/rifcs_format.rb
92
+ - lib/oai_repository.rb
93
+ - lib/oai_repository/engine.rb
94
+ - lib/oai_repository/version.rb
95
+ - lib/tasks/oai_repository_tasks.rake
96
+ - lib/oai_provider.rb
97
+ - lib/generators/oai_repository/install_generator.rb
98
+ - lib/generators/templates/oai_repository.rb
99
+ - lib/ar_wrapper_model.rb
100
+ - MIT-LICENSE
101
+ - Rakefile
102
+ - README.rdoc
103
+ homepage: https://github.com/IntersectAustralia/oai_repository
104
+ licenses: []
105
+ post_install_message:
106
+ rdoc_options: []
107
+ require_paths:
108
+ - lib
109
+ required_ruby_version: !ruby/object:Gem::Requirement
110
+ none: false
111
+ requirements:
112
+ - - ! '>='
113
+ - !ruby/object:Gem::Version
114
+ version: '0'
115
+ segments:
116
+ - 0
117
+ hash: -625295055
118
+ required_rubygems_version: !ruby/object:Gem::Requirement
119
+ none: false
120
+ requirements:
121
+ - - ! '>='
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ segments:
125
+ - 0
126
+ hash: -625295055
127
+ requirements: []
128
+ rubyforge_project:
129
+ rubygems_version: 1.8.15
130
+ signing_key:
131
+ specification_version: 3
132
+ summary: A Rails (3.1+) Engine to provide an OAI repository
133
+ test_files: []