qa 0.4.3 → 0.5.0

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.
Files changed (32) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +29 -13
  3. data/app/controllers/qa/terms_controller.rb +7 -3
  4. data/config/routes.rb +3 -3
  5. data/lib/qa.rb +2 -2
  6. data/lib/qa/authorities/authority_with_sub_authority.rb +17 -8
  7. data/lib/qa/authorities/base.rb +1 -1
  8. data/lib/qa/authorities/getty.rb +6 -52
  9. data/lib/qa/authorities/getty/aat.rb +55 -0
  10. data/lib/qa/authorities/loc.rb +9 -64
  11. data/lib/qa/authorities/loc/generic_authority.rb +71 -0
  12. data/lib/qa/authorities/local.rb +66 -31
  13. data/lib/qa/authorities/local/file_based_authority.rb +49 -0
  14. data/lib/qa/authorities/local_subauthority.rb +5 -5
  15. data/lib/qa/authorities/oclcts.rb +11 -32
  16. data/lib/qa/authorities/oclcts/generic_oclc_authority.rb +42 -0
  17. data/lib/qa/version.rb +1 -1
  18. data/spec/controllers/terms_controller_spec.rb +9 -9
  19. data/spec/internal/db/development.sqlite3 +0 -0
  20. data/spec/internal/log/development.log +10910 -0
  21. data/spec/lib/{authorities_local_spec.rb → authorities/file_based_authority_spec.rb} +8 -20
  22. data/spec/lib/{authorities_getty_spec.rb → authorities/getty/aat_spec.rb} +13 -19
  23. data/spec/lib/authorities/getty_spec.rb +30 -0
  24. data/spec/lib/{authorities_loc_spec.rb → authorities/loc_spec.rb} +12 -11
  25. data/spec/lib/authorities/local_spec.rb +48 -0
  26. data/spec/lib/{authorities_mesh_spec.rb → authorities/mesh_spec.rb} +0 -0
  27. data/spec/lib/{authorities_oclcts_spec.rb → authorities/oclcts_spec.rb} +2 -2
  28. data/spec/lib/{authorities_tgnlang_spec.rb → authorities/tgnlang_spec.rb} +0 -0
  29. data/spec/lib/authorities_loc_subauthorities.rb +3 -3
  30. data/spec/lib/authorities_local_subauthorities_spec.rb +3 -3
  31. data/spec/routing/route_spec.rb +3 -3
  32. metadata +22 -14
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: baa2933a1667d50c0e0f463f3ca2f123f9b680b2
4
- data.tar.gz: 5985db04fd4d2ea73fd11983897bf12e97bda22f
3
+ metadata.gz: 6edd488c2f33caee0d368664c08a2f4ea70f3b9e
4
+ data.tar.gz: 0bc9d51827a5ece5ce2bf71d8b184d232d859c6b
5
5
  SHA512:
6
- metadata.gz: 3033b94db5f5b5a650d3b4ceac9ac4807c4e6aed62f3d4c7253149dbb44fd1d951d9f4462a383e527260120e83b636b34d85d16ee65a475467d7c2665b361409
7
- data.tar.gz: d1804893f699995ac97ab3355f256b90899b1644e4dd59a0fc326539a8ed3389332dd813da2586672b8e6b0b752be2e31c39cbda3a5f1e16d44d96947daf0c94
6
+ metadata.gz: 592b389388e0fde9fd85996694ecb44a5bd477cbf78c4b10d066f757989a0d4a6d842d3832f8475bb4b77040afec66131db8ed8df52a4ae81de3789d7c7fffb1
7
+ data.tar.gz: 406889f087a33c4089b0f72e43e91f4a542e3569c29116f3657155f30a8591e6a8140c8785ec1fc70e9ac061b9dcb43c371a8b526ee15b5543dbe9fbcbe7b4e8
data/README.md CHANGED
@@ -51,17 +51,17 @@ Start questioning your authorities!
51
51
  Return a complete list of terms:
52
52
 
53
53
  /qa/terms/:vocab
54
- /qa/terms/:vocab/:sub_authority
54
+ /qa/terms/:vocab/:subauthority
55
55
 
56
56
  Return a set of terms matching a given query
57
57
 
58
58
  /qa/search/:vocab?q=search_term
59
- /qa/search/:vocab/:sub_authority?q=search_term
59
+ /qa/search/:vocab/:subauthority?q=search_term
60
60
 
61
61
  Return the complete information for a specific term given its identifier
62
62
 
63
63
  /qa/show/:vocab/:id
64
- /qa/show/:vocab/:sub_authority/:id
64
+ /qa/show/:vocab/:subauthority/:id
65
65
 
66
66
  ### JSON Results
67
67
 
@@ -136,7 +136,7 @@ authority YAML files are located in `config/authorities/`. This location can be
136
136
  the `:local_path` entry in `config/authorities.yml`. Relative paths are assumed to be relative to
137
137
  `Rails.root`.
138
138
 
139
- Local authority YAML files are named for the sub-authority they represent. The included example "states" sub-authority
139
+ Local authority YAML files are named for the sub-authority they represent. The included example "states" sub-authority
140
140
  is named states.yml.
141
141
 
142
142
  To create your own local authority, create a .yml file, place it in the configured directory and query it
@@ -148,19 +148,35 @@ using the file's name as the sub-authority. For example, if I create `foo.yml`,
148
148
 
149
149
  ##### List of terms
150
150
 
151
- :terms:
151
+ terms:
152
152
  - Term 1
153
153
  - Term 2
154
-
154
+
155
155
  ##### List of id and term keys and, optionally, active key
156
156
 
157
- :terms:
158
- - :id: id1
159
- :term: Term 1
160
- :active: true
161
- - :id: id2
162
- :term: Term 2
163
- :active: false
157
+ terms:
158
+ - id: id1
159
+ term: Term 1
160
+ active: true
161
+ - id: id2
162
+ term: Term 2
163
+ active: false
164
+
165
+
166
+ #### Adding your own local authorities
167
+
168
+ If you'd like to add your own local authority that isn't necessarily backed by yaml, create an initializer and tell the local authority about your custom sub-authority:
169
+
170
+ ```ruby
171
+ Qa::Authorities::Local.register_subauthority('names', 'LocalNames')
172
+ ```
173
+
174
+ The second argument is a name of a class that represents your local authority. Then when you go to:
175
+
176
+ /qa/search/local/names?q=Zoia
177
+
178
+ You'll be searching with an instance of `LocalNames`
179
+
164
180
 
165
181
  ### Medical Subject Headings (MeSH)
166
182
 
@@ -30,15 +30,19 @@ class Qa::TermsController < ApplicationController
30
30
 
31
31
  def init_authority
32
32
  begin
33
- klass = authority_class.constantize
33
+ mod = authority_class.constantize
34
34
  rescue NameError => e
35
35
  logger.warn "Unable to initialize authority #{authority_class}"
36
36
  head :not_found
37
37
  return
38
38
  end
39
- args = [params[:sub_authority]].compact
40
39
  begin
41
- @authority = klass.new(*args)
40
+ @authority = if mod.kind_of? Class
41
+ mod.new
42
+ else
43
+ raise Qa::MissingSubAuthority, "No sub-authority provided" if params[:subauthority].blank?
44
+ mod.subauthority_for(params[:subauthority])
45
+ end
42
46
  rescue Qa::InvalidSubAuthority, Qa::MissingSubAuthority => e
43
47
  logger.warn e.message
44
48
  head :not_found
@@ -1,6 +1,6 @@
1
1
  Qa::Engine.routes.draw do
2
- get "/terms/:vocab(/:sub_authority)", controller: :terms, action: :index
3
- get "/search/:vocab(/:sub_authority)", controller: :terms, action: :search
2
+ get "/terms/:vocab(/:subauthority)", controller: :terms, action: :index
3
+ get "/search/:vocab(/:subauthority)", controller: :terms, action: :search
4
4
  get "/show/:vocab/:id", controller: :terms, action: :show
5
- get "/show/:vocab/:sub_authority/:id", controller: :terms, action: :show
5
+ get "/show/:vocab/:subauthority/:id", controller: :terms, action: :show
6
6
  end
data/lib/qa.rb CHANGED
@@ -10,9 +10,9 @@ module Qa
10
10
  # Raised when the configuration directory for local authorities doesn't exist
11
11
  class ConfigDirectoryNotFound < StandardError; end
12
12
 
13
- # Raised when a sub_authority is not passed into an authority
13
+ # Raised when a subauthority is not passed into an authority
14
14
  class MissingSubAuthority < ArgumentError; end
15
15
 
16
- # Raised when a sub_authority is not valid
16
+ # Raised when a subauthority is not valid
17
17
  class InvalidSubAuthority < ArgumentError; end
18
18
  end
@@ -1,17 +1,26 @@
1
1
  module Qa::Authorities
2
- class AuthorityWithSubAuthority < Base
3
- attr_reader :sub_authority
2
+ module AuthorityWithSubAuthority
4
3
 
5
- # Registers the authority and its sub-authority if it has one
6
- def initialize(sub_authority=nil)
7
- @sub_authority = sub_authority
8
- raise Qa::MissingSubAuthority, "No sub-authority provided" if sub_authority.nil?
9
- raise Qa::InvalidSubAuthority, "Unable to initialize sub-authority #{sub_authority} for #{self.class.name}" unless sub_authorities.include?(sub_authority)
4
+ def new(subauthority=nil)
5
+ raise "Initializing with as sub authority is removed. use #{self.class}.subauthority_for(#{subauthority.inspect}) instead"
6
+ end
7
+
8
+ def subauthority_for(subauthority)
9
+ validate_subauthority!(subauthority)
10
+ subauthority_class(subauthority).new
11
+ end
12
+
13
+ def subauthority_class(name)
14
+ [self, name].join('::').classify.constantize
15
+ end
16
+
17
+ def validate_subauthority!(subauthority)
18
+ raise Qa::InvalidSubAuthority, "Unable to initialize sub-authority #{subauthority} for #{self}. Valid sub-authorities are #{subauthorities.inspect}" unless subauthorities.include?(subauthority)
10
19
  end
11
20
 
12
21
  # By default, an authority has no subauthorities unless they
13
22
  # are defined by the subclassed authority.
14
- def sub_authorities
23
+ def subauthorities
15
24
  []
16
25
  end
17
26
  end
@@ -18,7 +18,7 @@ module Qa::Authorities
18
18
  def find id
19
19
  end
20
20
 
21
- def full_record id, sub_authority=nil
21
+ def full_record id, subauthority=nil
22
22
  Deprecation.warn(".full_record is deprecated. Use .find instead")
23
23
  find(id)
24
24
  end
@@ -1,63 +1,17 @@
1
1
  require 'uri'
2
2
 
3
3
  module Qa::Authorities
4
- class Getty < AuthorityWithSubAuthority
5
- include WebServiceBase
4
+ module Getty
5
+ require 'qa/authorities/getty/aat'
6
+ extend AuthorityWithSubAuthority
6
7
 
7
- def sub_authorities
8
+ def self.subauthorities
8
9
  [ "aat" ]
9
10
  end
10
11
 
11
- def search q
12
- parse_authority_response(json(build_query_url(q)))
12
+ def self.subauthority_class(_)
13
+ AAT
13
14
  end
14
-
15
- # get_json is not ideomatic, so we'll make an alias
16
- def json(*args)
17
- get_json(*args)
18
- end
19
-
20
- def build_query_url q
21
- query = URI.escape(sparql(untaint(q)))
22
- "http://vocab.getty.edu/sparql.json?query=#{URI.escape(sparql(q))}&_implicit=false&implicit=true&_equivalent=false&_form=%2Fsparql"
23
- end
24
-
25
- def sparql(q)
26
- search = untaint(q)
27
- # The full text index matches on fields besides the term, so we filter to ensure the match is in the term.
28
- sparql = "SELECT ?s ?name {
29
- ?s a skos:Concept; luc:term \"#{search}\";
30
- skos:inScheme <http://vocab.getty.edu/#{@sub_authority}/> ;
31
- gvp:prefLabelGVP [skosxl:literalForm ?name].
32
- FILTER regex(?name, \"#{search}\", \"i\") .
33
- } LIMIT 10"
34
- end
35
-
36
- def untaint(q)
37
- q.gsub(/[^\w\s-]/, '')
38
- end
39
-
40
- def find id
41
- json(find_url(id))
42
- end
43
-
44
- def find_url id
45
- "http://vocab.getty.edu/#{@sub_authority}/#{id}.json"
46
- end
47
-
48
- def request_options
49
- { accept: 'application/sparql-results+json'}
50
- end
51
-
52
- private
53
-
54
- # Reformats the data received from the LOC service
55
- def parse_authority_response(response)
56
- response['results']['bindings'].map do |result|
57
- { 'id' => result['s']['value'], 'label' => result['name']['value'] }
58
- end
59
- end
60
-
61
15
  end
62
16
  end
63
17
 
@@ -0,0 +1,55 @@
1
+ module Qa::Authorities
2
+ class Getty::AAT < Base
3
+ include WebServiceBase
4
+ def search q
5
+ parse_authority_response(json(build_query_url(q)))
6
+ end
7
+
8
+ # get_json is not ideomatic, so we'll make an alias
9
+ def json(*args)
10
+ get_json(*args)
11
+ end
12
+
13
+ def build_query_url q
14
+ query = URI.escape(sparql(untaint(q)))
15
+ "http://vocab.getty.edu/sparql.json?query=#{URI.escape(sparql(q))}&_implicit=false&implicit=true&_equivalent=false&_form=%2Fsparql"
16
+ end
17
+
18
+ def sparql(q)
19
+ search = untaint(q)
20
+ # The full text index matches on fields besides the term, so we filter to ensure the match is in the term.
21
+ sparql = "SELECT ?s ?name {
22
+ ?s a skos:Concept; luc:term \"#{search}\";
23
+ skos:inScheme <http://vocab.getty.edu/aat/> ;
24
+ gvp:prefLabelGVP [skosxl:literalForm ?name].
25
+ FILTER regex(?name, \"#{search}\", \"i\") .
26
+ } LIMIT 10"
27
+ end
28
+
29
+ def untaint(q)
30
+ q.gsub(/[^\w\s-]/, '')
31
+ end
32
+
33
+ def find id
34
+ json(find_url(id))
35
+ end
36
+
37
+ def find_url id
38
+ "http://vocab.getty.edu/aat/#{id}.json"
39
+ end
40
+
41
+ def request_options
42
+ { accept: 'application/sparql-results+json'}
43
+ end
44
+
45
+ private
46
+
47
+ # Reformats the data received from the LOC service
48
+ def parse_authority_response(response)
49
+ response['results']['bindings'].map do |result|
50
+ { 'id' => result['s']['value'], 'label' => result['name']['value'] }
51
+ end
52
+ end
53
+
54
+ end
55
+ end
@@ -1,73 +1,18 @@
1
1
  require 'uri'
2
2
 
3
3
  module Qa::Authorities
4
- class Loc < AuthorityWithSubAuthority
5
- include WebServiceBase
6
- include LocSubauthority
4
+ module Loc
5
+ extend AuthorityWithSubAuthority
7
6
 
8
- def sub_authorities
9
- authorities + vocabularies + datatypes + preservation
10
- end
11
-
12
- def search q
13
- @raw_response = get_json(build_query_url(q))
14
- parse_authority_response
15
- end
16
-
17
- def build_query_url q
18
- escaped_query = URI.escape(q)
19
- authority_fragment = get_url_for_authority(sub_authority) + URI.escape(sub_authority)
20
- return "http://id.loc.gov/search/?q=#{escaped_query}&q=#{authority_fragment}&format=json"
21
- end
22
-
23
- def find id
24
- get_json(find_url(id))
25
- end
26
-
27
- def find_url id
28
- "http://id.loc.gov/authorities/#{@sub_authority}/#{id}.json"
7
+ require 'qa/authorities/loc/generic_authority'
8
+ def self.subauthority_for(subauthority)
9
+ validate_subauthority!(subauthority)
10
+ GenericAuthority.new(subauthority)
29
11
  end
30
12
 
31
- private
32
-
33
- # Reformats the data received from the LOC service
34
- def parse_authority_response
35
- @raw_response.select {|response| response[0] == "atom:entry"}.map do |response|
36
- loc_response_to_qa(response_to_struct(response))
37
- end
38
- end
39
-
40
- # Converts most of the atom data into an OpenStruct object.
41
- #
42
- # Note that this is a pretty naive conversion. There should probably just
43
- # be a class that properly translates and stores the various pieces of
44
- # data, especially if this logic could be useful in other auth lookups.
45
- def response_to_struct response
46
- result = response.each_with_object({}) do |result_parts, result|
47
- next unless result_parts[0]
48
- key = result_parts[0].sub('atom:', '').sub('dcterms:', '')
49
- info = result_parts[1]
50
- val = result_parts[2]
51
-
52
- case key
53
- when 'title', 'id', 'name', 'updated', 'created'
54
- result[key] = val
55
- when 'link'
56
- result["links"] ||= []
57
- result["links"] << [info["type"], info["href"]]
58
- end
59
- end
60
-
61
- OpenStruct.new(result)
62
- end
63
-
64
- # Simple conversion from LoC-based struct to QA hash
65
- def loc_response_to_qa data
66
- {
67
- "id" => data.id || data.title,
68
- "label" => data.title
69
- }
13
+ extend LocSubauthority
14
+ def self.subauthorities
15
+ authorities + vocabularies + datatypes + preservation
70
16
  end
71
-
72
17
  end
73
18
  end
@@ -0,0 +1,71 @@
1
+ module Qa::Authorities
2
+ class Loc::GenericAuthority < Base
3
+ attr_reader :subauthority
4
+ def initialize(subauthority)
5
+ @subauthority = subauthority
6
+ end
7
+
8
+ include WebServiceBase
9
+
10
+ def search q
11
+ @raw_response = get_json(build_query_url(q))
12
+ parse_authority_response
13
+ end
14
+
15
+ def build_query_url q
16
+ escaped_query = URI.escape(q)
17
+ authority_fragment = Loc.get_url_for_authority(subauthority) + URI.escape(subauthority)
18
+ return "http://id.loc.gov/search/?q=#{escaped_query}&q=#{authority_fragment}&format=json"
19
+ end
20
+
21
+ def find id
22
+ get_json(find_url(id))
23
+ end
24
+
25
+ def find_url id
26
+ "http://id.loc.gov/authorities/#{@subauthority}/#{id}.json"
27
+ end
28
+
29
+ private
30
+
31
+ # Reformats the data received from the LOC service
32
+ def parse_authority_response
33
+ @raw_response.select {|response| response[0] == "atom:entry"}.map do |response|
34
+ loc_response_to_qa(response_to_struct(response))
35
+ end
36
+ end
37
+
38
+ # Converts most of the atom data into an OpenStruct object.
39
+ #
40
+ # Note that this is a pretty naive conversion. There should probably just
41
+ # be a class that properly translates and stores the various pieces of
42
+ # data, especially if this logic could be useful in other auth lookups.
43
+ def response_to_struct response
44
+ result = response.each_with_object({}) do |result_parts, result|
45
+ next unless result_parts[0]
46
+ key = result_parts[0].sub('atom:', '').sub('dcterms:', '')
47
+ info = result_parts[1]
48
+ val = result_parts[2]
49
+
50
+ case key
51
+ when 'title', 'id', 'name', 'updated', 'created'
52
+ result[key] = val
53
+ when 'link'
54
+ result["links"] ||= []
55
+ result["links"] << [info["type"], info["href"]]
56
+ end
57
+ end
58
+
59
+ OpenStruct.new(result)
60
+ end
61
+
62
+ # Simple conversion from LoC-based struct to QA hash
63
+ def loc_response_to_qa data
64
+ {
65
+ "id" => data.id || data.title,
66
+ "label" => data.title
67
+ }
68
+ end
69
+
70
+ end
71
+ end