qa-ldf 0.1.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 (43) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/.rubocop.yml +21 -0
  4. data/.travis.yml +15 -0
  5. data/.yardopts +9 -0
  6. data/CONTRIBUTING.md +109 -0
  7. data/Gemfile +18 -0
  8. data/Guardfile +27 -0
  9. data/LICENSE +14 -0
  10. data/README.md +42 -0
  11. data/Rakefile +30 -0
  12. data/VERSION +1 -0
  13. data/config/ldf.yml +15 -0
  14. data/lib/qa/ldf.rb +43 -0
  15. data/lib/qa/ldf/authorities.rb +1 -0
  16. data/lib/qa/ldf/authorities/lc_names.rb +31 -0
  17. data/lib/qa/ldf/authority.rb +104 -0
  18. data/lib/qa/ldf/client.rb +39 -0
  19. data/lib/qa/ldf/configuration.rb +67 -0
  20. data/lib/qa/ldf/empty_search_service.rb +18 -0
  21. data/lib/qa/ldf/json_mapper.rb +35 -0
  22. data/lib/qa/ldf/model.rb +49 -0
  23. data/lib/qa/ldf/spec.rb +3 -0
  24. data/lib/qa/ldf/spec/authority.rb +112 -0
  25. data/lib/qa/ldf/spec/model.rb +9 -0
  26. data/lib/qa/ldf/spec/search_service.rb +30 -0
  27. data/lib/qa/ldf/version.rb +34 -0
  28. data/qa-ldf.gemspec +35 -0
  29. data/spec/contracts/qa_loc_as_search_service_spec.rb +38 -0
  30. data/spec/qa/ldf/authorities/lc_names_spec.rb +52 -0
  31. data/spec/qa/ldf/authority_spec.rb +29 -0
  32. data/spec/qa/ldf/client_spec.rb +23 -0
  33. data/spec/qa/ldf/configuration_spec.rb +93 -0
  34. data/spec/qa/ldf/empty_search_service_spec.rb +11 -0
  35. data/spec/qa/ldf/json_mapper_spec.rb +22 -0
  36. data/spec/qa/ldf/model_spec.rb +53 -0
  37. data/spec/qa/ldf_spec.rb +43 -0
  38. data/spec/spec_helper.rb +18 -0
  39. data/spec/support/cache_server.rb +25 -0
  40. data/spec/support/fake_client.rb +31 -0
  41. data/spec/support/fake_search_service.rb +40 -0
  42. data/spec/support/shared_examples/ld_cache_client.rb +35 -0
  43. metadata +232 -0
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+ require 'qa/authorities/loc'
3
+
4
+ # We need to guarantee `Loc.subauthority_for('names')` behaves like a search
5
+ # service
6
+ describe Qa::Authorities::Loc do
7
+ subject(:search_service) { described_class.subauthority_for('names') }
8
+ before do
9
+ # an empty response for 'tove'
10
+ stub_request(:get, 'http://id.loc.gov/search/?format=json&'\
11
+ 'q=tove&q=cs:http://id.loc.gov/authorities/names')
12
+ .with(headers: { 'Accept' => 'application/json' })
13
+ .to_return(status: 200, body: '[]', headers: {})
14
+
15
+ # real responses from fixtures
16
+ search_config.each do |search|
17
+ stub_request(:get, 'http://id.loc.gov/search/?format=json' \
18
+ "&q=#{search[:query]}&q=cs:" \
19
+ 'http://id.loc.gov/authorities/names')
20
+ .with(headers: { 'Accept' => 'application/json' })
21
+ .to_return(status: 200, body: search[:body], headers: {})
22
+ end
23
+ end
24
+
25
+ let(:search_config) do
26
+ [
27
+ { query: 'moomin', body: '[]', result: [] }, # an empty body
28
+ ]
29
+ end
30
+
31
+ let(:searches) do
32
+ search_config.each_with_object({}) do |search, hsh|
33
+ hsh[search[:query]] = search[:result]
34
+ end
35
+ end
36
+
37
+ it_behaves_like 'an ldf search service'
38
+ end
@@ -0,0 +1,52 @@
1
+ require 'spec_helper'
2
+
3
+ describe Qa::LDF::LCNames do
4
+ it_behaves_like 'an ldf authority'
5
+
6
+ subject(:authority) do
7
+ auth = described_class.new
8
+
9
+ auth.client = FakeClient.new do |client|
10
+ client.graph = RDF::Graph.new
11
+ client.graph.insert(*statements)
12
+ client.label = ldf_label
13
+ end
14
+
15
+ auth
16
+ end
17
+
18
+ let(:ldf_label) { 'Marble Island (Nunavut)' }
19
+ let(:ldf_uri) { 'http://id.loc.gov/authorities/subjects/sh2004002557' }
20
+
21
+ let(:statements) do
22
+ [RDF::Statement(RDF::URI(ldf_uri),
23
+ RDF::Vocab::SKOS.prefLabel,
24
+ RDF::Literal(ldf_label))]
25
+ end
26
+
27
+ before do
28
+ # mock empty responses for all queries;
29
+ # see spec/contracts/qa_loc_as_search_service.rb for lc search service tests
30
+ stub_request(:get, 'http://id.loc.gov/search/?format=json&' \
31
+ 'q=cs:http://id.loc.gov/authorities/names')
32
+ .with(headers: { 'Accept' => 'application/json' })
33
+ .to_return(status: 200, body: '[]', headers: {})
34
+ end
35
+
36
+ describe '#search_service' do
37
+ it 'hits the upstream loc endpoint by default' do
38
+ query = 'a query'
39
+ url = 'http://id.loc.gov/search/?format=json' \
40
+ "&q=#{query}&q=cs:http://id.loc.gov/authorities/names"
41
+ expect(a_request(:get, url))
42
+
43
+ authority.search_service.search(query)
44
+ end
45
+ end
46
+
47
+ describe '#dataset' do
48
+ it 'defaults to :lcnames' do
49
+ expect(described_class.new.dataset).to eq :lcnames
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ describe Qa::LDF::Authority do
4
+ it_behaves_like 'an ldf authority'
5
+
6
+ subject(:authority) do
7
+ auth = described_class.new
8
+
9
+ auth.client = FakeClient.new do |client|
10
+ client.graph = RDF::Graph.new << statement
11
+ client.label = ldf_label
12
+ end
13
+
14
+ auth
15
+ end
16
+
17
+ let(:ldf_label) { 'Marble Island (Nunavut)' }
18
+ let(:ldf_uri) { 'http://id.loc.gov/authorities/subjects/sh2004002557' }
19
+
20
+ let(:statement) do
21
+ [RDF::URI(ldf_uri), RDF::Vocab::SKOS.prefLabel, RDF::Literal(ldf_label)]
22
+ end
23
+
24
+ describe '#dataset' do
25
+ it 'defaults an empty string symbol' do
26
+ expect(described_class.new.dataset).to eq :''
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+ ##
3
+ # @todo: Stub LDF behavior at this point. Add integration/contract tests for
4
+ # this interface.
5
+ describe Qa::LDF::Client do
6
+ subject(:client) { described_class.new }
7
+
8
+ let(:uri) { 'http://id.loc.gov/authorities/subjects/sh2004002557' }
9
+ let(:graph_stub) { RDF::Graph.new << [RDF::URI(uri), RDF.type, RDF.Property] }
10
+
11
+ before do
12
+ # stub the external request, this behavior is owned by the server,
13
+ # we just allow it to make the request
14
+ stub_request(:get, uri)
15
+ .to_return(status: 200,
16
+ body: graph_stub.dump(:ntriples),
17
+ headers: { 'Content-Type' =>
18
+ RDF::Format.for(:ntriples).content_type })
19
+ end
20
+
21
+ include_context 'with cache server'
22
+ it_behaves_like 'an ld cache client'
23
+ end
@@ -0,0 +1,93 @@
1
+ require 'spec_helper'
2
+
3
+ describe Qa::LDF::Configuration do
4
+ subject(:config) { described_class.instance }
5
+
6
+ shared_context 'with configuration' do
7
+ before { config.configure!(**options) }
8
+ after { config.reset! }
9
+
10
+ let(:options) do
11
+ { option: :moomin }
12
+ end
13
+ end
14
+
15
+ describe '#[]' do
16
+ include_context 'with configuration'
17
+
18
+ it 'gives nil for unconfigured options' do
19
+ expect(config[:fake]).to be_nil
20
+ end
21
+
22
+ it 'accesses options' do
23
+ options.each { |k, v| expect(config[k]).to eq v }
24
+ end
25
+ end
26
+
27
+ describe '#configure!' do
28
+ it 'configures the options' do
29
+ expect { config.configure!(key: :value) }
30
+ .to change { config[:key] }.from(nil).to(:value)
31
+ end
32
+
33
+ it 'overrides the options' do
34
+ config.configure!(key: :value)
35
+
36
+ expect { config.configure!(key: :new_value) }
37
+ .to change { config[:key] }.from(:value).to(:new_value)
38
+ end
39
+
40
+ it 'overrides options not in new config' do
41
+ config.configure!(key: :value)
42
+
43
+ expect { config.configure!(new_key: :new_value) }
44
+ .to change { config[:key] }.from(:value).to(nil)
45
+ end
46
+
47
+ it 'yields itself' do
48
+ expect { |b| config.configure!(&b) }.to yield_with_args(config)
49
+ end
50
+
51
+ it 'yields configured self' do
52
+ options = { key1: :value, key2: :value }
53
+
54
+ config.configure!(**options) { |c| expect(c.to_h).to eq options }
55
+ end
56
+
57
+ it 'returns self' do
58
+ expect(config.configure!).to eql config
59
+ end
60
+ end
61
+
62
+ describe '#each' do
63
+ include_context 'with configuration'
64
+
65
+ it 'enumerates the options hash' do
66
+ expect(config.each).to contain_exactly(*options.each)
67
+ end
68
+ end
69
+
70
+ describe '#reset!' do
71
+ include_context 'with configuration'
72
+
73
+ it 'resets options hash' do
74
+ expect { config.reset! }.to change { config.to_a }.to be_empty
75
+ end
76
+ end
77
+
78
+ describe '#to_a' do
79
+ include_context 'with configuration'
80
+
81
+ it 'returns the configured options as an array' do
82
+ expect(config.to_a).to eq options.to_a
83
+ end
84
+ end
85
+
86
+ describe '#to_h' do
87
+ include_context 'with configuration'
88
+
89
+ it 'returns the configured options as a hash' do
90
+ expect(config.to_h).to eq options
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,11 @@
1
+ # coding: utf-8
2
+ require 'spec_helper'
3
+ require 'qa/ldf/empty_search_service'
4
+
5
+ describe Qa::LDF::EmptySearchService do
6
+ it_behaves_like 'an ldf search service'
7
+
8
+ subject(:search_service) { described_class.new }
9
+
10
+ let(:searches) { { '-NonSensE\ !QUERY' => [], 'Bărăganul (Romania)' => [] } }
11
+ end
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+
3
+ describe Qa::LDF::JsonMapper do
4
+ subject(:mapper) { described_class.new }
5
+
6
+ let(:uri) { 'http://example.com/moomin' }
7
+ let(:label) { RDF::Literal('Moomin') }
8
+
9
+ let(:graph) do
10
+ RDF::Graph.new << [RDF::URI(uri), RDF::Vocab::SKOS.prefLabel, label]
11
+ end
12
+
13
+ describe '#map_resource' do
14
+ it 'maps with an id' do
15
+ expect(mapper.map_resource(uri, graph)[:id]).to eq uri
16
+ end
17
+
18
+ it 'maps with a label' do
19
+ expect(mapper.map_resource(uri, graph)[:label]).to eq label
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,53 @@
1
+ require 'spec_helper'
2
+
3
+ describe Qa::LDF::Model do
4
+ define :be_term do |expected|
5
+ match { |actual| expect(actual.to_term).to eq expected }
6
+ end
7
+
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
53
+ end
@@ -0,0 +1,43 @@
1
+ require 'spec_helper'
2
+
3
+ describe Qa::LDF do
4
+ subject(:mod) { described_class }
5
+
6
+ it 'has a version' do
7
+ expect(mod::VERSION.to_str).to be_a String
8
+ end
9
+
10
+ describe '#version' do
11
+ it 'returns VERISON' do
12
+ expect(mod.version).to eql mod::VERSION
13
+ end
14
+ end
15
+
16
+ describe '.config' do
17
+ it 'defaults to an empty config' do
18
+ expect(mod.config).not_to be_any
19
+ end
20
+
21
+ context 'when configured' do
22
+ before { mod.configure!(**options) }
23
+
24
+ let(:options) { { opt: :value, opt2: :value2 } }
25
+
26
+ it 'returns the configuration instance' do
27
+ expect(mod.config.to_h).to eq options
28
+ end
29
+ end
30
+ end
31
+
32
+ describe '#configure!' do
33
+ it 'dispatches arguments to the configuration instance' do
34
+ opts = { opt: :value }
35
+ block = proc {}
36
+
37
+ expect(Qa::LDF::Configuration.instance)
38
+ .to receive(:configure!).with(**opts, &block).once
39
+
40
+ mod.configure!(**opts, &block)
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+ require 'bundler/setup'
3
+ Bundler.setup
4
+
5
+ require 'pry' unless ENV['CI']
6
+ require 'webmock/rspec'
7
+
8
+ require 'qa/ldf'
9
+ require 'qa/ldf/spec'
10
+
11
+ Dir['./spec/support/**/*.rb'].each { |f| require f }
12
+
13
+ RSpec.configure do |config|
14
+ config.color = true
15
+ config.tty = true
16
+
17
+ config.formatter = :progress
18
+ end
@@ -0,0 +1,25 @@
1
+ require 'sham_rack'
2
+
3
+ require 'linked_data_fragments/cache_server'
4
+ require 'linked_data_fragments/repository'
5
+
6
+ shared_context 'with cache server' do
7
+ let(:server_endpoint) { 'http://ldcache.example.com/' }
8
+
9
+ before(:context) do
10
+ server_endpoint = 'ldcache.example.com'
11
+
12
+ ShamRack
13
+ .at(server_endpoint)
14
+ .mount(LinkedDataFragments::CacheServer::APPLICATION)
15
+
16
+ Qa::LDF.configure! do |config|
17
+ config[:endpoint] = "http://#{server_endpoint}"
18
+ end
19
+ end
20
+
21
+ after(:context) do
22
+ ShamRack.reset
23
+ Qa::LDF::Configuration.instance.reset!
24
+ end
25
+ end
@@ -0,0 +1,31 @@
1
+ require 'support/shared_examples/ld_cache_client'
2
+
3
+ require 'rdf'
4
+ require 'rdf/vocab/skos'
5
+
6
+ ##
7
+ # A fake version of `Qa::LDF::Client`.
8
+ class FakeClient
9
+ attr_accessor :label, :graph
10
+
11
+ def initialize
12
+ yield self if block_given?
13
+ end
14
+
15
+ def get(uri:, dataset: :'')
16
+ graph ||= RDF::Graph.new
17
+ # Use dataset just to satisfy rubocop.
18
+ # Is there a better config setting for this; exclude this cop from fakes?
19
+ graph =
20
+ graph.dup <<
21
+ [RDF::URI(uri), RDF::Vocab::SKOS.prefLabel, label || "#{dataset}: moomin"]
22
+ graph
23
+ end
24
+ end
25
+
26
+ describe FakeClient do
27
+ subject(:client) { described_class.new }
28
+
29
+ let(:uri) { 'http://id.loc.gov/authorities/subjects/sh2004002557' }
30
+ it_behaves_like 'an ld cache client'
31
+ end