qa 0.4.3 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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