qa-ldf 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a949f884516d5b051b47a5b14d23c67043a0d3d3
4
- data.tar.gz: 50ca4ecf8a10304ac8a2afa38afd38264c537ebe
3
+ metadata.gz: 855a7a374246e7b3deb9e2a05f275a7e1ac4b39b
4
+ data.tar.gz: 6fceb2d6ec1511192f388a7da4edcd37b223088a
5
5
  SHA512:
6
- metadata.gz: 5ef5befc68d69cf514995bdacaea034fdbb1c03b0c4effe9898f91cd57547ae26585414b40bc158171ae7bb3850ca06c42b5157dac15bd581b2ff106e5e59c22
7
- data.tar.gz: 5a1d78db92bfc2de9d80b60292cb5dc0c1d3cc72bc210885529bcd56510ec6a455079e3a9d97df5538056d12cd6844a3d40245bba54bb4acabb10b05073a831e
6
+ metadata.gz: e3c38a54bd7ec95604666dc08113e18d8f240976b21f2d39dbb40445d954455860dbf79494dd4811083e754494864fd54cd78582f4707b53f7dfdbc23a8e976e
7
+ data.tar.gz: 951a494528c90e84f66df9747a810e3b9a0a92fb9a6643058e91de475885b3b42c8d12017295daaa918e7c3ebefc3c165776ef7ddd881370301749c49ac7392f
data/.rubocop.yml CHANGED
@@ -8,6 +8,12 @@ Metric/BlockLength:
8
8
  - 'spec/**/*'
9
9
  - 'lib/qa/ldf/spec/**/*'
10
10
 
11
+ # Class variables are fine. We want the "nasty" behavior described by rubocop
12
+ # in some cases.
13
+ # https://github.com/bbatsov/ruby-style-guide/blob/master/README.md#no-class-vars
14
+ Style/ClassVars:
15
+ Enabled: false
16
+
11
17
  # we accept `expect(subject).to receive`
12
18
  RSpec/MessageSpies:
13
19
  Enabled: false
@@ -19,3 +25,8 @@ RSpec/FilePath:
19
25
  - 'spec/support/**/*'
20
26
  - 'spec/qa/ldf/authorities/**/*'
21
27
  - 'spec/qa/ldf/models/**/*'
28
+
29
+ # we allow non-Class describes for integration
30
+ RSpec/DescribeClass:
31
+ Exclude:
32
+ - 'spec/integration/**/*'
data/.travis.yml CHANGED
@@ -1,5 +1,6 @@
1
1
  language: ruby
2
2
  sudo: false
3
+ script: 'bundle exec rake ci environment=test'
3
4
 
4
5
  cache:
5
6
  bundler: true
data/Gemfile CHANGED
@@ -10,9 +10,6 @@ end
10
10
 
11
11
  gem 'pry' unless ENV['CI']
12
12
 
13
- # develop on qa master
14
- gem 'qa', github: 'projecthydra-labs/questioning_authority', branch: 'master'
15
-
16
13
  gem 'ld_cache_fragment',
17
14
  github: 'ActiveTriples/linked-data-fragments',
18
15
  branch: 'feature/multi-dataset'
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.0
1
+ 0.2.0
@@ -10,8 +10,18 @@ module Qa
10
10
  # @see LinkedDataFragments::CacheServer
11
11
  class LCNames < Authority
12
12
  DEFAULT_DATASET_NAME = :lcnames
13
+ NAMESPACE = 'http://id.loc.gov/authorities/names/'.freeze
13
14
  LC_SUBAUTHORITY = 'names'.freeze
14
15
 
16
+ register_namespace(namespace: NAMESPACE,
17
+ klass: self)
18
+
19
+ ##
20
+ # @return [String] the URI namespace associated with this authority
21
+ def self.namespace
22
+ NAMESPACE
23
+ end
24
+
15
25
  ##
16
26
  # Uses the LC names subauthority as the search provider
17
27
  def search_service
@@ -26,6 +26,52 @@ module Qa
26
26
  # The default search service
27
27
  DEFAULT_SEARCH_SERVICE = Qa::LDF::EmptySearchService
28
28
 
29
+ class << self
30
+ @@namespace_map = {}
31
+
32
+ ##
33
+ # Gives an authority class for the given namespace.
34
+ #
35
+ # @param namespace [#to_s] a URI-like namespace string
36
+ #
37
+ # @return [Authority]
38
+ def for(namespace:)
39
+ @@namespace_map.fetch(namespace.to_s) { self }.new
40
+ end
41
+
42
+ ##
43
+ # @return [String] gives an empty string for no namespace,
44
+ # {#for_namespace} returns self in this case
45
+ def namespace
46
+ ''
47
+ end
48
+
49
+ ##
50
+ # @return [Enumerable<String>]
51
+ def namespaces
52
+ @@namespace_map.keys
53
+ end
54
+
55
+ ##
56
+ # Registers a namespace/class pair.
57
+ #
58
+ # @param namespace [#to_s] a URI-like namespace string
59
+ # @param klass [Class] an authority class
60
+ #
61
+ # @return [void]
62
+ def register_namespace(namespace:, klass:)
63
+ @@namespace_map[namespace.to_s] = klass
64
+ end
65
+
66
+ ##
67
+ # Resets the namespaces
68
+ #
69
+ # @return [void]
70
+ def reset_namespaces
71
+ @@namespace_map = {}
72
+ end
73
+ end
74
+
29
75
  ##
30
76
  # @!attribute [rw] client
31
77
  # @return [Client]
@@ -60,9 +106,14 @@ module Qa
60
106
  # @see Qa::Authorities::Base#find
61
107
  # @see Qa::LDF::Client#get
62
108
  def find(id)
63
- graph = client.get(uri: id, dataset: dataset)
109
+ mapper.map_resource(id, graph(id))
110
+ end
64
111
 
65
- mapper.map_resource(id, graph)
112
+ ##
113
+ # @param uri [RDF::URI]
114
+ # @return [RDF::Enumerable]
115
+ def graph(uri)
116
+ client.get(uri: uri, dataset: dataset)
66
117
  end
67
118
 
68
119
  ##
data/lib/qa/ldf/model.rb CHANGED
@@ -7,6 +7,60 @@ module Qa
7
7
  class Model
8
8
  include ActiveTriples::RDFSource
9
9
 
10
+ ##
11
+ # @return [Qa::LDF::Authority]
12
+ #
13
+ # @example
14
+ # # Regester the namespace with the authority class.
15
+ # class MyAuthority < Authority
16
+ # register_namespace(namespace: 'http://example.com/my_authority#',
17
+ # klass: self)
18
+ # end
19
+ #
20
+ # model = Qa::LDF::Model.new('http://example.com/my_authority#moomin')
21
+ #
22
+ # model.authority # => #<MyAuthority:0xbad1dea>
23
+ #
24
+ def authority
25
+ Qa::LDF::Authority.for(namespace: authority_namespace)
26
+ end
27
+
28
+ ##
29
+ # @return [String] the namespace for the authority used by this model
30
+ # instance.
31
+ #
32
+ # @example
33
+ # # Regester the namespace with the authority class.
34
+ # class MyAuthority < Authority
35
+ # register_namespace(namespace: 'http://example.com/my_authority#',
36
+ # klass: self)
37
+ # end
38
+ #
39
+ # model = Qa::LDF::Model.new('http://example.com/my_authority#moomin')
40
+ #
41
+ # model.authority_namespace # => 'http://example.com/my_authority#'
42
+ #
43
+ def authority_namespace
44
+ return Qa::LDF::Authority.namespace if node?
45
+
46
+ Qa::LDF::Authority
47
+ .namespaces
48
+ .find { |ns| to_uri.start_with?(ns) }
49
+ end
50
+
51
+ ##
52
+ # Fetches from the cache client.
53
+ #
54
+ # @see ActiveTriples::RDFSource#fetch
55
+ def fetch
56
+ insert(authority.graph(to_uri))
57
+ rescue => e
58
+ raise e unless block_given?
59
+ yield(self)
60
+ ensure
61
+ self
62
+ end
63
+
10
64
  class << self
11
65
  ##
12
66
  # Builds a model from the graph.
@@ -25,6 +25,19 @@ shared_examples 'an ldf authority' do
25
25
  end
26
26
  end
27
27
 
28
+ it 'is registered' do
29
+ expect(Qa::LDF::Authority.for(namespace: described_class.namespace))
30
+ .to be_a described_class
31
+ end
32
+
33
+ describe '.namespace' do
34
+ it 'returns a namespace string' do
35
+ unless described_class.namespace == ''
36
+ expect(RDF::URI(described_class.namespace)).to be_valid
37
+ end
38
+ end
39
+ end
40
+
28
41
  describe '#all' do
29
42
  it 'is enumerable' do
30
43
  expect(authority.all).to respond_to :each
@@ -72,6 +85,15 @@ shared_examples 'an ldf authority' do
72
85
  it 'maps to a json-friendly hash' do
73
86
  expect { JSON.generate(authority.find(ldf_uri)) }.not_to raise_error
74
87
  end
88
+
89
+ context 'when dataset is not assigned' do
90
+ before { authority.dataset = nil }
91
+
92
+ it 'finds a uri' do
93
+ expect(authority.find(ldf_uri))
94
+ .to include id: ldf_uri.to_s, label: ldf_label
95
+ end
96
+ end
75
97
  end
76
98
 
77
99
  describe '#mapper' do
@@ -5,5 +5,111 @@ shared_examples 'an ldf model' do
5
5
  "#{model} must be defined with `let(:model)` before " \
6
6
  'using Qa::LDF::Model shared examples.'
7
7
  end
8
+
9
+ unless defined?(id)
10
+ raise ArgumentError,
11
+ "#{id} must be defined with `let(:id)` before " \
12
+ 'using Qa::LDF::Model shared examples.'
13
+ end
14
+ end
15
+
16
+ let(:label) { 'moomin' }
17
+
18
+ define :be_term do |expected|
19
+ match { |actual| expect(actual.to_term).to eq expected }
20
+ end
21
+
22
+ describe '.from_graph' do
23
+ let(:graph) do
24
+ graph = RDF::Graph.new
25
+ graph.insert(*statements)
26
+ graph
27
+ end
28
+
29
+ let(:statements) do
30
+ [RDF::Statement(RDF::URI(id), RDF::Vocab::SKOS.prefLabel, label),
31
+ RDF::Statement(RDF::URI(id), RDF::Vocab::DC.title, 'Moomin Papa'),
32
+ RDF::Statement(RDF::Node.new, RDF::Vocab::DC.title, 'Moomin Papa')]
33
+ end
34
+
35
+ it 'builds a model with the id as uri' do
36
+ expect(described_class.from_graph(uri: id, graph: graph))
37
+ .to be_term RDF::URI(id)
38
+ end
39
+
40
+ it 'builds a model with the label as label' do
41
+ expect(described_class.from_graph(uri: id, graph: graph))
42
+ .to have_attributes rdf_label: a_collection_containing_exactly(label)
43
+ end
44
+
45
+ it 'contains passed graph' do
46
+ expect(described_class.from_graph(uri: id, graph: graph).statements)
47
+ .to include(*statements)
48
+ end
49
+ end
50
+
51
+ describe '.from_qa_result' do
52
+ let(:json_hash) { { id: id, label: label, papa: 'moomin papa' } }
53
+
54
+ it 'builds a model with the id as uri' do
55
+ expect(described_class.from_qa_result(qa_result: json_hash))
56
+ .to be_term RDF::URI(id)
57
+ end
58
+
59
+ it 'builds a model with the label as label' do
60
+ expect(described_class.from_qa_result(qa_result: json_hash))
61
+ .to have_attributes rdf_label: a_collection_containing_exactly(label)
62
+ end
63
+ end
64
+
65
+ describe '#authority' do
66
+ it 'returns an authority' do
67
+ expect(model.authority).to respond_to(:find).with(1).argument
68
+ end
69
+
70
+ it 'returns the base authority for undefined namespace' do
71
+ expect(model.authority.dataset)
72
+ .to eq Qa::LDF::Authority::DEFAULT_DATASET_NAME
73
+ end
74
+
75
+ context 'with a namespaced uri' do
76
+ let(:ns) { 'http://id.loc.gov/authorities/names/' }
77
+ let(:id) { ns + 'n83175996' }
78
+ let(:klass) { Class.new(Qa::LDF::Authority) }
79
+
80
+ before do
81
+ Qa::LDF::Authority.register_namespace(namespace: ns, klass: klass)
82
+ end
83
+
84
+ after { Qa::LDF::Authority.reset_namespaces }
85
+
86
+ it 'knows its authority' do
87
+ expect(model.authority).to be_a klass
88
+ end
89
+ end
90
+ end
91
+
92
+ describe '#fetch' do
93
+ let(:ns) { 'http://example.com/moomin_auth/' }
94
+ let(:id) { ns + 'moomin' }
95
+
96
+ let(:klass) do
97
+ Class.new(Qa::LDF::Authority) do
98
+ register_namespace(namespace: 'http://example.com/moomin_auth',
99
+ klass: self)
100
+
101
+ def initialize
102
+ @client = FakeClient.new
103
+ end
104
+ end
105
+ end
106
+
107
+ before { klass }
108
+
109
+ it 'populates the graph from the client' do
110
+ expect { model.fetch }
111
+ .to change { model.statements }
112
+ .from(be_empty)
113
+ end
8
114
  end
9
115
  end
data/qa-ldf.gemspec CHANGED
@@ -24,6 +24,7 @@ Gem::Specification.new do |spec|
24
24
 
25
25
  spec.add_dependency 'qa', '~> 0.11.0'
26
26
 
27
+ spec.add_development_dependency 'active-fedora', '~> 11.0'
27
28
  spec.add_development_dependency 'guard', '~> 2.14'
28
29
  spec.add_development_dependency 'ld_cache_fragment', '~> 0.1'
29
30
  spec.add_development_dependency 'rake', '~> 12.0'
@@ -0,0 +1,57 @@
1
+ require 'spec_helper'
2
+ require 'active_fedora'
3
+
4
+ # Tests the main boundaries to ActiveFedora. This should be considered
5
+ # incomplete, at best. We haven't exhastively analyzed relevant AF behavior.
6
+ describe 'ActiveFedora integration' do
7
+ subject(:af_model) { af_model_class.new }
8
+
9
+ let(:value_uri) { RDF::URI('http://id.loc.gov/authorities/names/n83175996') }
10
+ let(:af_model_class) do
11
+ Class.new(ActiveFedora::Base) do
12
+ property :programmer,
13
+ predicate: RDF::Vocab::MARCRelators.prg,
14
+ class_name: Qa::LDF::Model
15
+ end
16
+ end
17
+
18
+ before { af_model.programmer = [value_uri] }
19
+
20
+ define :be_term do |expected|
21
+ match { |actual| expect(actual.to_term).to eq expected }
22
+ end
23
+
24
+ it 'builds value as a model' do
25
+ expect(af_model.programmer)
26
+ .to contain_exactly(an_instance_of(Qa::LDF::Model))
27
+ end
28
+
29
+ it 'builds value with correct rdf term' do
30
+ af_model.programmer = [value_uri]
31
+ expect(af_model.programmer).to contain_exactly(be_term(value_uri))
32
+ end
33
+
34
+ describe 'retrieving from the server' do
35
+ include_context 'with cache server'
36
+
37
+ let(:statement) do
38
+ RDF::Statement(value_uri, RDF::URI('http://example.com/p'), :o)
39
+ end
40
+
41
+ let(:stub) do
42
+ stub_request(:get, value_uri.to_s)
43
+ .to_return(body: statement.to_ntriples,
44
+ headers: { 'Content-Type' => 'text/turtle' })
45
+ end
46
+
47
+ # touch the stub
48
+ before { stub }
49
+
50
+ it 'value can retrieve its graph from the cache' do
51
+ af_model.programmer = [value_uri]
52
+
53
+ af_model.programmer.first.fetch
54
+ expect(stub).to have_been_requested
55
+ end
56
+ end
57
+ end
@@ -14,6 +14,16 @@ describe Qa::LDF::Authority do
14
14
  auth
15
15
  end
16
16
 
17
+ shared_context 'with registered namespaces' do
18
+ let(:ns) { 'http://example.com/auth/' }
19
+ let(:subclass) { Class.new(described_class) }
20
+
21
+ before do
22
+ subclass.register_namespace(namespace: ns, klass: subclass)
23
+ end
24
+ after { subclass.reset_namespaces }
25
+ end
26
+
17
27
  let(:ldf_label) { 'Marble Island (Nunavut)' }
18
28
  let(:ldf_uri) { 'http://id.loc.gov/authorities/subjects/sh2004002557' }
19
29
 
@@ -21,6 +31,57 @@ describe Qa::LDF::Authority do
21
31
  [RDF::URI(ldf_uri), RDF::Vocab::SKOS.prefLabel, RDF::Literal(ldf_label)]
22
32
  end
23
33
 
34
+ describe '.for' do
35
+ let(:ns) { 'http://example.com/auth/' }
36
+ let(:subclass) { Class.new(described_class) }
37
+
38
+ before { subclass.register_namespace(namespace: ns, klass: subclass) }
39
+ after { subclass.reset_namespaces }
40
+
41
+ it 'returns registered classes from namespaces' do
42
+ expect(described_class.for(namespace: ns)).to be_a subclass
43
+ end
44
+ end
45
+
46
+ describe '.namespaces' do
47
+ it 'returns empty namespaces list' do
48
+ expect(described_class.namespaces).to be_empty
49
+ end
50
+
51
+ context 'when namespaces are registered' do
52
+ include_context 'with registered namespaces'
53
+
54
+ it 'returns populated namespaces list' do
55
+ expect(described_class.namespaces).to contain_exactly ns
56
+ end
57
+ end
58
+ end
59
+
60
+ describe '.register_namespace' do
61
+ let(:ns) { 'http://example.com/auth/' }
62
+ let(:subclass) { Class.new(described_class) }
63
+
64
+ after { subclass.reset_namespaces }
65
+
66
+ it 'adds to namespace registry' do
67
+ expect { subclass.register_namespace(namespace: ns, klass: subclass) }
68
+ .to change { described_class.for(namespace: ns) }
69
+ .from(described_class)
70
+ .to(subclass)
71
+ end
72
+ end
73
+
74
+ describe '.reset_namespaces' do
75
+ include_context 'with registered namespaces'
76
+
77
+ it 'clears the namespace list' do
78
+ expect { subclass.reset_namespaces }
79
+ .to change { described_class.for(namespace: ns) }
80
+ .from(subclass)
81
+ .to(described_class)
82
+ end
83
+ end
84
+
24
85
  describe '#dataset' do
25
86
  it 'defaults an empty string symbol' do
26
87
  expect(described_class.new.dataset).to eq :''
@@ -1,53 +1,8 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Qa::LDF::Model do
4
- define :be_term do |expected|
5
- match { |actual| expect(actual.to_term).to eq expected }
6
- end
4
+ subject(:model) { described_class.new(id) }
5
+ let(:id) { 'http://example.com/authority/moomin' }
7
6
 
8
- let(:id) { 'http://example.com/authority/moomin' }
9
- let(:label) { 'moomin' }
10
-
11
- describe '.from_graph' do
12
- let(:graph) do
13
- graph = RDF::Graph.new
14
- graph.insert(*statements)
15
- graph
16
- end
17
-
18
- let(:statements) do
19
- [RDF::Statement(RDF::URI(id), RDF::Vocab::SKOS.prefLabel, label),
20
- RDF::Statement(RDF::URI(id), RDF::Vocab::DC.title, 'Moomin Papa'),
21
- RDF::Statement(RDF::Node.new, RDF::Vocab::DC.title, 'Moomin Papa')]
22
- end
23
-
24
- it 'builds a model with the id as uri' do
25
- expect(described_class.from_graph(uri: id, graph: graph))
26
- .to be_term RDF::URI(id)
27
- end
28
-
29
- it 'builds a model with the label as label' do
30
- expect(described_class.from_graph(uri: id, graph: graph))
31
- .to have_attributes rdf_label: a_collection_containing_exactly(label)
32
- end
33
-
34
- it 'contains passed graph' do
35
- expect(described_class.from_graph(uri: id, graph: graph).statements)
36
- .to include(*statements)
37
- end
38
- end
39
-
40
- describe '.from_qa_result' do
41
- let(:json_hash) { { id: id, label: label, papa: 'moomin papa' } }
42
-
43
- it 'builds a model with the id as uri' do
44
- expect(described_class.from_qa_result(qa_result: json_hash))
45
- .to be_term RDF::URI(id)
46
- end
47
-
48
- it 'builds a model with the label as label' do
49
- expect(described_class.from_qa_result(qa_result: json_hash))
50
- .to have_attributes rdf_label: a_collection_containing_exactly(label)
51
- end
52
- end
7
+ it_behaves_like 'an ldf model'
53
8
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: qa-ldf
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tom Johnson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-02-23 00:00:00.000000000 Z
11
+ date: 2017-03-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: qa
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: 0.11.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: active-fedora
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '11.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '11.0'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: guard
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -177,6 +191,7 @@ files:
177
191
  - lib/qa/ldf/version.rb
178
192
  - qa-ldf.gemspec
179
193
  - spec/contracts/qa_loc_as_search_service_spec.rb
194
+ - spec/integration/active_fedora_model_spec.rb
180
195
  - spec/qa/ldf/authorities/lc_names_spec.rb
181
196
  - spec/qa/ldf/authority_spec.rb
182
197
  - spec/qa/ldf/client_spec.rb
@@ -217,6 +232,7 @@ summary: Provides a bridge between questioning authority and a caching linked da
217
232
  fragment for URI authorities.
218
233
  test_files:
219
234
  - spec/contracts/qa_loc_as_search_service_spec.rb
235
+ - spec/integration/active_fedora_model_spec.rb
220
236
  - spec/qa/ldf/authorities/lc_names_spec.rb
221
237
  - spec/qa/ldf/authority_spec.rb
222
238
  - spec/qa/ldf/client_spec.rb