krikri 0.10.1 → 0.11.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 (118) hide show
  1. checksums.yaml +4 -4
  2. data/app/models/krikri/original_record.rb +1 -0
  3. data/lib/krikri/async_uri_getter.rb +127 -0
  4. data/lib/krikri/engine.rb +18 -0
  5. data/lib/krikri/ldp.rb +3 -2
  6. data/lib/krikri/ldp/invalidatable.rb +107 -0
  7. data/lib/krikri/ldp/rdf_source.rb +24 -6
  8. data/lib/krikri/ldp/resource.rb +22 -1
  9. data/lib/krikri/map_crosswalk.rb +5 -2
  10. data/lib/krikri/version.rb +1 -1
  11. data/spec/internal/Gemfile.lock +4 -4
  12. data/spec/internal/config/initializers/blacklight_initializer.rb +1 -1
  13. data/spec/internal/config/initializers/devise.rb +2 -2
  14. data/spec/internal/config/secrets.yml +2 -2
  15. data/spec/internal/db/development.sqlite3 +0 -0
  16. data/spec/internal/db/migrate/{20151222175607_devise_create_users.rb → 20160113172004_devise_create_users.rb} +0 -0
  17. data/spec/internal/db/migrate/{20151222175629_create_searches.blacklight.rb → 20160113172024_create_searches.blacklight.rb} +0 -0
  18. data/spec/internal/db/migrate/{20151222175630_create_bookmarks.blacklight.rb → 20160113172025_create_bookmarks.blacklight.rb} +0 -0
  19. data/spec/internal/db/migrate/{20151222175631_add_polymorphic_type_to_bookmarks.blacklight.rb → 20160113172026_add_polymorphic_type_to_bookmarks.blacklight.rb} +0 -0
  20. data/spec/internal/db/schema.rb +1 -1
  21. data/spec/internal/db/test.sqlite3 +0 -0
  22. data/spec/internal/log/development.log +84 -84
  23. data/spec/internal/log/test.log +5709 -3532
  24. data/spec/lib/krikri/async_uri_getter_spec.rb +106 -0
  25. data/spec/lib/krikri/map_crosswalk_spec.rb +18 -0
  26. data/spec/lib/krikri/search_index_spec.rb +22 -14
  27. data/spec/models/dpla/map/aggregation_spec.rb +21 -0
  28. data/spec/models/original_record_spec.rb +3 -0
  29. data/spec/support/shared_contexts/entities_query.rb +1 -0
  30. data/spec/support/shared_examples/ldp_invalidatable.rb +98 -0
  31. data/spec/support/shared_examples/rdf_source.rb +2 -1
  32. metadata +18 -184
  33. data/spec/internal/db/test.sqlite3-journal +0 -0
  34. data/spec/internal/tmp/cache/assets/test/sass/4a3ee647961c7e45976eb2c0a94406aad3427b3d/_alerts.scssc +0 -0
  35. data/spec/internal/tmp/cache/assets/test/sass/4a3ee647961c7e45976eb2c0a94406aad3427b3d/_background-variant.scssc +0 -0
  36. data/spec/internal/tmp/cache/assets/test/sass/4a3ee647961c7e45976eb2c0a94406aad3427b3d/_border-radius.scssc +0 -0
  37. data/spec/internal/tmp/cache/assets/test/sass/4a3ee647961c7e45976eb2c0a94406aad3427b3d/_buttons.scssc +0 -0
  38. data/spec/internal/tmp/cache/assets/test/sass/4a3ee647961c7e45976eb2c0a94406aad3427b3d/_center-block.scssc +0 -0
  39. data/spec/internal/tmp/cache/assets/test/sass/4a3ee647961c7e45976eb2c0a94406aad3427b3d/_clearfix.scssc +0 -0
  40. data/spec/internal/tmp/cache/assets/test/sass/4a3ee647961c7e45976eb2c0a94406aad3427b3d/_forms.scssc +0 -0
  41. data/spec/internal/tmp/cache/assets/test/sass/4a3ee647961c7e45976eb2c0a94406aad3427b3d/_gradients.scssc +0 -0
  42. data/spec/internal/tmp/cache/assets/test/sass/4a3ee647961c7e45976eb2c0a94406aad3427b3d/_grid-framework.scssc +0 -0
  43. data/spec/internal/tmp/cache/assets/test/sass/4a3ee647961c7e45976eb2c0a94406aad3427b3d/_grid.scssc +0 -0
  44. data/spec/internal/tmp/cache/assets/test/sass/4a3ee647961c7e45976eb2c0a94406aad3427b3d/_hide-text.scssc +0 -0
  45. data/spec/internal/tmp/cache/assets/test/sass/4a3ee647961c7e45976eb2c0a94406aad3427b3d/_image.scssc +0 -0
  46. data/spec/internal/tmp/cache/assets/test/sass/4a3ee647961c7e45976eb2c0a94406aad3427b3d/_labels.scssc +0 -0
  47. data/spec/internal/tmp/cache/assets/test/sass/4a3ee647961c7e45976eb2c0a94406aad3427b3d/_list-group.scssc +0 -0
  48. data/spec/internal/tmp/cache/assets/test/sass/4a3ee647961c7e45976eb2c0a94406aad3427b3d/_nav-divider.scssc +0 -0
  49. data/spec/internal/tmp/cache/assets/test/sass/4a3ee647961c7e45976eb2c0a94406aad3427b3d/_nav-vertical-align.scssc +0 -0
  50. data/spec/internal/tmp/cache/assets/test/sass/4a3ee647961c7e45976eb2c0a94406aad3427b3d/_opacity.scssc +0 -0
  51. data/spec/internal/tmp/cache/assets/test/sass/4a3ee647961c7e45976eb2c0a94406aad3427b3d/_pagination.scssc +0 -0
  52. data/spec/internal/tmp/cache/assets/test/sass/4a3ee647961c7e45976eb2c0a94406aad3427b3d/_panels.scssc +0 -0
  53. data/spec/internal/tmp/cache/assets/test/sass/4a3ee647961c7e45976eb2c0a94406aad3427b3d/_progress-bar.scssc +0 -0
  54. data/spec/internal/tmp/cache/assets/test/sass/4a3ee647961c7e45976eb2c0a94406aad3427b3d/_reset-filter.scssc +0 -0
  55. data/spec/internal/tmp/cache/assets/test/sass/4a3ee647961c7e45976eb2c0a94406aad3427b3d/_resize.scssc +0 -0
  56. data/spec/internal/tmp/cache/assets/test/sass/4a3ee647961c7e45976eb2c0a94406aad3427b3d/_responsive-visibility.scssc +0 -0
  57. data/spec/internal/tmp/cache/assets/test/sass/4a3ee647961c7e45976eb2c0a94406aad3427b3d/_size.scssc +0 -0
  58. data/spec/internal/tmp/cache/assets/test/sass/4a3ee647961c7e45976eb2c0a94406aad3427b3d/_tab-focus.scssc +0 -0
  59. data/spec/internal/tmp/cache/assets/test/sass/4a3ee647961c7e45976eb2c0a94406aad3427b3d/_table-row.scssc +0 -0
  60. data/spec/internal/tmp/cache/assets/test/sass/4a3ee647961c7e45976eb2c0a94406aad3427b3d/_text-emphasis.scssc +0 -0
  61. data/spec/internal/tmp/cache/assets/test/sass/4a3ee647961c7e45976eb2c0a94406aad3427b3d/_text-overflow.scssc +0 -0
  62. data/spec/internal/tmp/cache/assets/test/sass/4a3ee647961c7e45976eb2c0a94406aad3427b3d/_vendor-prefixes.scssc +0 -0
  63. data/spec/internal/tmp/cache/assets/test/sass/93e201cf4a11978a1f491a057a3bd569c3825210/blacklight.css.scssc +0 -0
  64. data/spec/internal/tmp/cache/assets/test/sass/a1ec1bb9c9cafeb054d542e861ebc8ffd5904439/_alerts.scssc +0 -0
  65. data/spec/internal/tmp/cache/assets/test/sass/a1ec1bb9c9cafeb054d542e861ebc8ffd5904439/_badges.scssc +0 -0
  66. data/spec/internal/tmp/cache/assets/test/sass/a1ec1bb9c9cafeb054d542e861ebc8ffd5904439/_breadcrumbs.scssc +0 -0
  67. data/spec/internal/tmp/cache/assets/test/sass/a1ec1bb9c9cafeb054d542e861ebc8ffd5904439/_button-groups.scssc +0 -0
  68. data/spec/internal/tmp/cache/assets/test/sass/a1ec1bb9c9cafeb054d542e861ebc8ffd5904439/_buttons.scssc +0 -0
  69. data/spec/internal/tmp/cache/assets/test/sass/a1ec1bb9c9cafeb054d542e861ebc8ffd5904439/_carousel.scssc +0 -0
  70. data/spec/internal/tmp/cache/assets/test/sass/a1ec1bb9c9cafeb054d542e861ebc8ffd5904439/_close.scssc +0 -0
  71. data/spec/internal/tmp/cache/assets/test/sass/a1ec1bb9c9cafeb054d542e861ebc8ffd5904439/_code.scssc +0 -0
  72. data/spec/internal/tmp/cache/assets/test/sass/a1ec1bb9c9cafeb054d542e861ebc8ffd5904439/_component-animations.scssc +0 -0
  73. data/spec/internal/tmp/cache/assets/test/sass/a1ec1bb9c9cafeb054d542e861ebc8ffd5904439/_dropdowns.scssc +0 -0
  74. data/spec/internal/tmp/cache/assets/test/sass/a1ec1bb9c9cafeb054d542e861ebc8ffd5904439/_forms.scssc +0 -0
  75. data/spec/internal/tmp/cache/assets/test/sass/a1ec1bb9c9cafeb054d542e861ebc8ffd5904439/_glyphicons.scssc +0 -0
  76. data/spec/internal/tmp/cache/assets/test/sass/a1ec1bb9c9cafeb054d542e861ebc8ffd5904439/_grid.scssc +0 -0
  77. data/spec/internal/tmp/cache/assets/test/sass/a1ec1bb9c9cafeb054d542e861ebc8ffd5904439/_input-groups.scssc +0 -0
  78. data/spec/internal/tmp/cache/assets/test/sass/a1ec1bb9c9cafeb054d542e861ebc8ffd5904439/_jumbotron.scssc +0 -0
  79. data/spec/internal/tmp/cache/assets/test/sass/a1ec1bb9c9cafeb054d542e861ebc8ffd5904439/_labels.scssc +0 -0
  80. data/spec/internal/tmp/cache/assets/test/sass/a1ec1bb9c9cafeb054d542e861ebc8ffd5904439/_list-group.scssc +0 -0
  81. data/spec/internal/tmp/cache/assets/test/sass/a1ec1bb9c9cafeb054d542e861ebc8ffd5904439/_media.scssc +0 -0
  82. data/spec/internal/tmp/cache/assets/test/sass/a1ec1bb9c9cafeb054d542e861ebc8ffd5904439/_mixins.scssc +0 -0
  83. data/spec/internal/tmp/cache/assets/test/sass/a1ec1bb9c9cafeb054d542e861ebc8ffd5904439/_modals.scssc +0 -0
  84. data/spec/internal/tmp/cache/assets/test/sass/a1ec1bb9c9cafeb054d542e861ebc8ffd5904439/_navbar.scssc +0 -0
  85. data/spec/internal/tmp/cache/assets/test/sass/a1ec1bb9c9cafeb054d542e861ebc8ffd5904439/_navs.scssc +0 -0
  86. data/spec/internal/tmp/cache/assets/test/sass/a1ec1bb9c9cafeb054d542e861ebc8ffd5904439/_normalize.scssc +0 -0
  87. data/spec/internal/tmp/cache/assets/test/sass/a1ec1bb9c9cafeb054d542e861ebc8ffd5904439/_pager.scssc +0 -0
  88. data/spec/internal/tmp/cache/assets/test/sass/a1ec1bb9c9cafeb054d542e861ebc8ffd5904439/_pagination.scssc +0 -0
  89. data/spec/internal/tmp/cache/assets/test/sass/a1ec1bb9c9cafeb054d542e861ebc8ffd5904439/_panels.scssc +0 -0
  90. data/spec/internal/tmp/cache/assets/test/sass/a1ec1bb9c9cafeb054d542e861ebc8ffd5904439/_popovers.scssc +0 -0
  91. data/spec/internal/tmp/cache/assets/test/sass/a1ec1bb9c9cafeb054d542e861ebc8ffd5904439/_print.scssc +0 -0
  92. data/spec/internal/tmp/cache/assets/test/sass/a1ec1bb9c9cafeb054d542e861ebc8ffd5904439/_progress-bars.scssc +0 -0
  93. data/spec/internal/tmp/cache/assets/test/sass/a1ec1bb9c9cafeb054d542e861ebc8ffd5904439/_responsive-embed.scssc +0 -0
  94. data/spec/internal/tmp/cache/assets/test/sass/a1ec1bb9c9cafeb054d542e861ebc8ffd5904439/_responsive-utilities.scssc +0 -0
  95. data/spec/internal/tmp/cache/assets/test/sass/a1ec1bb9c9cafeb054d542e861ebc8ffd5904439/_scaffolding.scssc +0 -0
  96. data/spec/internal/tmp/cache/assets/test/sass/a1ec1bb9c9cafeb054d542e861ebc8ffd5904439/_tables.scssc +0 -0
  97. data/spec/internal/tmp/cache/assets/test/sass/a1ec1bb9c9cafeb054d542e861ebc8ffd5904439/_thumbnails.scssc +0 -0
  98. data/spec/internal/tmp/cache/assets/test/sass/a1ec1bb9c9cafeb054d542e861ebc8ffd5904439/_tooltip.scssc +0 -0
  99. data/spec/internal/tmp/cache/assets/test/sass/a1ec1bb9c9cafeb054d542e861ebc8ffd5904439/_type.scssc +0 -0
  100. data/spec/internal/tmp/cache/assets/test/sass/a1ec1bb9c9cafeb054d542e861ebc8ffd5904439/_utilities.scssc +0 -0
  101. data/spec/internal/tmp/cache/assets/test/sass/a1ec1bb9c9cafeb054d542e861ebc8ffd5904439/_variables.scssc +0 -0
  102. data/spec/internal/tmp/cache/assets/test/sass/a1ec1bb9c9cafeb054d542e861ebc8ffd5904439/_wells.scssc +0 -0
  103. data/spec/internal/tmp/cache/assets/test/sass/b28605b1c659cf09fc72f3c1fff32918869d28b8/_bootstrap-sprockets.scssc +0 -0
  104. data/spec/internal/tmp/cache/assets/test/sass/b28605b1c659cf09fc72f3c1fff32918869d28b8/_bootstrap.scssc +0 -0
  105. data/spec/internal/tmp/cache/assets/test/sass/ca8c12d03785e0d6cd4554f4d3939e7836d38282/_blacklight_base.scssc +0 -0
  106. data/spec/internal/tmp/cache/assets/test/sass/ca8c12d03785e0d6cd4554f4d3939e7836d38282/blacklight.scssc +0 -0
  107. data/spec/internal/tmp/cache/assets/test/sprockets/4052820c15af72ba690230a0f92bd75e +0 -0
  108. data/spec/internal/tmp/cache/assets/test/sprockets/496a0d7dce1ff6bf4a9c3a089ea3a635 +0 -0
  109. data/spec/internal/tmp/cache/assets/test/sprockets/50b9db0b908b421a9b941a445dbaeacc +0 -0
  110. data/spec/internal/tmp/cache/assets/test/sprockets/528c628cf107f8be6dd122e1154344be +0 -0
  111. data/spec/internal/tmp/cache/assets/test/sprockets/8edfca9082e02111be92e79000667f22 +0 -0
  112. data/spec/internal/tmp/cache/assets/test/sprockets/90b54a819800edfa41b67722d1561040 +0 -0
  113. data/spec/internal/tmp/cache/assets/test/sprockets/9c653367feff82588eb6041d783a5809 +0 -0
  114. data/spec/internal/tmp/cache/assets/test/sprockets/b35e12934e9f05662777579549e31cd7 +0 -0
  115. data/spec/internal/tmp/cache/assets/test/sprockets/bb108ef3fc4c96d1c20cc41f97d943a0 +0 -0
  116. data/spec/internal/tmp/cache/assets/test/sprockets/dbba4bbc32c17ade3d618c5d0baeb371 +0 -0
  117. data/spec/internal/tmp/cache/assets/test/sprockets/e9f7ccc553ce1a217709cc7a08cfb032 +0 -0
  118. data/spec/internal/tmp/cache/assets/test/sprockets/f274b5f22db177b6464b50691d531688 +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0fc07861777f021a9d02ccb7c9be507cd7f8681d
4
- data.tar.gz: 894be25e402c7aea81b7f82708ee8971e86bf97f
3
+ metadata.gz: 5aa4fcca719417b8f265da3ee528f69d5fcbc3ec
4
+ data.tar.gz: d505e82b5881a9fae1e3f65b85b19157a6083bae
5
5
  SHA512:
6
- metadata.gz: e9ef1dc88f88c4ac8e2ae167758549309c79b0332a29bad230d199fcc9518c03c07832900a51eed2e497b7283b1725c113c6e4080ee805303a7b1bb26370f7d5
7
- data.tar.gz: 26b4f8a38a3bf313e8b2fce1a35474feb3fd19b6fe401817f05f98948b4ac99dae8d466134ae1c31f163facd1c7add886a5e8d67ab1d110659f5398f754efabe
6
+ metadata.gz: 197ad8e9e763ce9abb331dc57b9bca7a12103484107f6b27002adc946a8a5244eb27b1756e5dbf8e37881977a59a0ba541639c5485ed9ff11f55f3453362902f
7
+ data.tar.gz: 0c328d43f14401964ec57725850d813cf50892b6c11f3e71fc74c2fa318b8b96c83a8eec8dde3542211b9d70787eb6322ae8c7d4ddac8bee916fee6adf8aaca3
@@ -3,6 +3,7 @@ module Krikri
3
3
  # Handles records as harvested, prior to mapping
4
4
  class OriginalRecord
5
5
  include Krikri::LDP::Resource
6
+ include Krikri::LDP::Invalidatable
6
7
 
7
8
  attr_accessor :content, :local_name, :rdf_subject
8
9
  attr_writer :content_type
@@ -0,0 +1,127 @@
1
+ require 'faraday'
2
+ require 'faraday_middleware'
3
+
4
+ require 'net/http'
5
+ require 'thread'
6
+ require 'uri'
7
+
8
+ module Krikri
9
+ ##
10
+ # Helper class for fetching multiple URLs concurrently.
11
+ #
12
+ # @example to fetch 5 URLs in 5 threads
13
+ # urls = ['http://example.com/one',
14
+ # 'http://example.com/two',
15
+ # 'http://example.com/three',
16
+ # 'http://example.com/four',
17
+ # 'http://example.com/five']
18
+ # .map { |url| URI.parse(url) }
19
+ #
20
+ # getter = Krikri::AsyncUriGetter.new
21
+ #
22
+ # requests = urls.map do |url|
23
+ # getter.add_request(uri: url, opts: { follow_redirects: true })
24
+ # end
25
+ #
26
+ # At this point, 5 threads are launched to fetch the list of URLs. We can
27
+ # wait for them all to finish if we want to make sure we don't continue until
28
+ # all threads have terminated: `requests.map(&:join)`
29
+ #
30
+ # Or simply access the responses and have our current thread block until
31
+ # they're available:
32
+ #
33
+ # requests.each do |request|
34
+ # request.with_response do |response|
35
+ # if response.status == 200
36
+ # puts "Response body: #{response.body}"
37
+ # else
38
+ # puts "Got return status: #{response.status}"
39
+ # end
40
+ # end
41
+ # end
42
+ #
43
+ class AsyncUriGetter
44
+ MAX_REDIRECTS = 10
45
+
46
+ ##
47
+ # Create a new asynchronous URL fetcher.
48
+ #
49
+ # @param opts [Hash] a hash of the supported options, which are:
50
+ # @option opts [Boolean] :follow_redirects Whether to follow HTTP 3xx
51
+ # redirects.
52
+ # @option opts [Integer] :max_redirects Number of redirects to follow before
53
+ # giving up. (default: 10)
54
+ def initialize(opts: {})
55
+ @default_opts = { max_redirects: MAX_REDIRECTS }.merge(opts)
56
+ end
57
+
58
+ ##
59
+ # Run a request (in a new thread) and return a promise-like object for the
60
+ # response.
61
+ #
62
+ # @param uri [URI] the URI to be fetched
63
+ # @param headers [Hash<String, String>] HTTP headers to include with the
64
+ # request
65
+ # @param opts [Hash] options to override the ones provided when
66
+ # AsyncUriGetter was initialized. All supported options from `#initialize`
67
+ # are available here as well.
68
+ def add_request(uri: nil, headers: {}, opts: {})
69
+ fail ArgumentError, "uri must be a URI; got: #{uri}" unless uri.is_a?(URI)
70
+ Request.new(uri, headers, @default_opts.merge(opts))
71
+ end
72
+
73
+ Request = Struct.new(:uri, :headers, :opts) do
74
+ def initialize(*)
75
+ super
76
+ @request_thread = start_request
77
+ end
78
+
79
+ ##
80
+ # Wait for the request thread to complete
81
+ def join
82
+ @request_thread.join
83
+ end
84
+
85
+ ##
86
+ # @yield [Faraday::Response] the response returned for the request
87
+ def with_response
88
+ yield @request_thread.value
89
+ end
90
+
91
+ private
92
+
93
+ ##
94
+ # Run the Faraday request in a new thread
95
+ def start_request
96
+ Thread.new do
97
+ http.get(uri) do |request|
98
+ headers.each { |header, val| request.headers[header.to_s] = val }
99
+ end
100
+ end
101
+ end
102
+
103
+ ##
104
+ # @return [Faraday::Connection] a connection with sensible defaults
105
+ def http
106
+ @http ||= Faraday.new do |conn|
107
+ conn.request :retry,
108
+ max: 4,
109
+ interval: 0.025,
110
+ interval_randomness: 0.5,
111
+ backoff_factor: 2,
112
+ exceptions: [Faraday::ConnectionFailed,
113
+ 'Errno::ETIMEDOUT',
114
+ 'Timeout::Error',
115
+ 'Error::TimeoutError',
116
+ Faraday::TimeoutError]
117
+ if opts.fetch(:follow_redirects, false)
118
+ conn.use(FaradayMiddleware::FollowRedirects,
119
+ limit: opts.fetch(:max_redirects))
120
+ end
121
+
122
+ conn.adapter Faraday.default_adapter
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
data/lib/krikri/engine.rb CHANGED
@@ -83,6 +83,12 @@ module Krikri
83
83
  end
84
84
 
85
85
  initializer :aggregation do
86
+ class NamespaceError < RuntimeError
87
+ def initialize(uri)
88
+ super("Tried to get DPLA ID for non-DPLA URI #{uri}")
89
+ end
90
+ end
91
+
86
92
  DPLA::MAP::Aggregation.class_eval do
87
93
  include Krikri::MapCrosswalk
88
94
  include Krikri::LDP::RdfSource
@@ -124,6 +130,18 @@ module Krikri
124
130
  .to_s)
125
131
  end
126
132
 
133
+ ##
134
+ # @return [String, nil] returns only the final portion of the URI (the
135
+ # "local name"), with the `#base_uri` removed. `nil` if this is a node
136
+ #
137
+ # @raise NamespaceError
138
+ def dpla_id
139
+ return nil if node?
140
+ raise NamespaceError, rdf_subject unless id.start_with?(base_uri)
141
+
142
+ id.gsub("#{base_uri}/", '')
143
+ end
144
+
127
145
  private
128
146
 
129
147
  def local_name_from_original_record
data/lib/krikri/ldp.rb CHANGED
@@ -4,7 +4,8 @@ module Krikri
4
4
  # As LDP support develops, it might be possible to excract this or replace it
5
5
  # with a tool like the `ldp` gem.
6
6
  module LDP
7
- autoload :Resource, 'krikri/ldp/resource'
8
- autoload :RdfSource, 'krikri/ldp/rdf_source'
7
+ autoload :Resource, 'krikri/ldp/resource'
8
+ autoload :RdfSource, 'krikri/ldp/rdf_source'
9
+ autoload :Invalidatable, 'krikri/ldp/invalidatable'
9
10
  end
10
11
  end
@@ -0,0 +1,107 @@
1
+ module Krikri::LDP
2
+ ##
3
+ # Implements invalidation for `Krikri::LDP::Resource`s. This is different
4
+ # from deletion, in that the resource continues to respond `200 OK`, and
5
+ # return the representation, Nothing is removed from the LDP server.
6
+ #
7
+ # Works as a mixin to `Krikri::LDP::Resource`, assuming an implementation of
8
+ # `#rdf_source`, which may simply return `self`.
9
+ #
10
+ # @example invalidating a resource
11
+ # class MyResource
12
+ # include Krikri::LDP::Resource
13
+ # include Krikri::LDP::Invalidatable
14
+ #
15
+ # def rdf_subject
16
+ # @rdf_subject ||= RDF::URI('http://example.com/ldp/a/resource/path')
17
+ # end
18
+ # end
19
+ #
20
+ # invalidatable_resource = MyResource.new
21
+ # # the resource must exist before it can be invalidated!
22
+ # invalidatable_resource.save
23
+ #
24
+ # invalidatable_resource.invalidate!
25
+ # invalidatable_resource.invalidated? # => true
26
+ # invalidatable_resource.invalidated_at_time
27
+ # # => Thu, 03 Dec 2015 10:27:45 -0800
28
+ #
29
+ # @see http://www.w3.org/TR/2013/REC-prov-dm-20130430/#term-Invalidation
30
+ # for documentation on PROV invalidation
31
+ module Invalidatable
32
+ # @see RDF::PROV
33
+ INVALIDATED_BY_URI = RDF::PROV.wasInvalidatedBy
34
+ INVALIDATED_TIME_URI = RDF::PROV.invalidatedAtTime
35
+
36
+ ##
37
+ # Invalidates the resource by marking it with a `prov:invalidatedAtTime`. If
38
+ # an `RDF::Term` is passed as the first argument, that term is used as the
39
+ # value of `prov:wasInvalidatedBy`.
40
+ #
41
+ # @example invalidating with an activity
42
+ # invalidatable_resource.invalidate!(RDF::URI('http://example.org/moomin'))
43
+ # invalidatable_resource.was_invalidated_by
44
+ # # => #<RDF::URI:0x2acab846109c URI:http://example.org/moomin>
45
+ #
46
+ # @param activity_uri [RDF::Term] a URI for the invalidating activity. If
47
+ # none is given, this defaults to `nil` and no `prov:wasInvalidatedBy`
48
+ # statement is added.
49
+ # @param ignore_invalid [Boolean] if true, supresses errors on already,
50
+ # invalid records
51
+ #
52
+ # @raise [RuntimeError] when the resource does not exist or is already
53
+ # invalid; unless `ignore_invalid` is `true`
54
+ # @return [void]
55
+ def invalidate!(activity_uri = nil, ignore_invalid = false)
56
+ raise "Cannot invalidate #{rdf_subject}, does not exist." unless exists?
57
+
58
+ # force a reload unless we have cached an invalidatedAtTime
59
+ rdf_source.get({}, true) unless invalidated?
60
+ # we check invalidated again in case the reload came back invalid
61
+ if invalidated?
62
+ return if ignore_invalid
63
+ raise "Cannot invalidate #{rdf_subject}, already invalid."
64
+ end
65
+
66
+ uri = RDF::URI(rdf_subject)
67
+
68
+ rdf_source << [uri, INVALIDATED_BY_URI, activity_uri] unless
69
+ activity_uri.nil?
70
+ rdf_source << [uri, INVALIDATED_TIME_URI, DateTime.now]
71
+
72
+ rdf_source.save
73
+ end
74
+
75
+ ##
76
+ # @return [Boolean] `true` if the resource has been marked invalidated.
77
+ def invalidated?
78
+ !invalidated_at_time.nil?
79
+ end
80
+
81
+ ##
82
+ # @return [DateTime, nil] the time this resource was marked invalidated;
83
+ # gives `nil` if the resource has not been invalidated.
84
+ #
85
+ # @note if two invalidatedAtTimes exist, we may get either of them back!
86
+ def invalidated_at_time
87
+ time = first_property(INVALIDATED_TIME_URI)
88
+ time.nil? ? nil : time.object
89
+ end
90
+
91
+ ##
92
+ # @return [RDF::URI, nil] the activity responsible for invalidating the
93
+ # resource
94
+ #
95
+ # @note if two wasInvalidatedBys exist, we may get either of them back!
96
+ def was_invalidated_by
97
+ first_property(INVALIDATED_BY_URI)
98
+ end
99
+
100
+ private
101
+
102
+ def first_property(predicate)
103
+ res = rdf_source.query([RDF::URI(rdf_subject), predicate, nil])
104
+ res.empty? ? nil : res.first.object
105
+ end
106
+ end
107
+ end
@@ -7,13 +7,26 @@ module Krikri::LDP
7
7
  module RdfSource
8
8
  extend ActiveSupport::Concern
9
9
  include Krikri::LDP::Resource
10
+ include Krikri::LDP::Invalidatable
11
+
12
+ GENERATED_URI = RDF::PROV.wasGeneratedBy
13
+ REVISED_URI = RDF::DPLA.wasRevisedBy
14
+
15
+ ##
16
+ # @return [Boolean] false if this resource does not ex
17
+ # @see Krikri::LDP::Resource#exists?
18
+ def exists?
19
+ return false if node?
20
+ super
21
+ end
22
+ alias_method :exist?, :exists?
10
23
 
11
24
  ##
12
25
  # PUTs the LDP resource named in #rdf_subject, populating it's content
13
26
  # (graph) from the object's RDF::Graph.
14
27
  #
15
28
  # @see Krikri::LDP::Resource#save
16
- # @note this may leave the resource's graph out of sync with the LDP
29
+ # @note this may leave the resource's graph out of sync with the LDP
17
30
  # endpoint since the endpoint may add management triples when saving.
18
31
  def save(*)
19
32
  result = super(dump(:ttl))
@@ -21,14 +34,14 @@ module Krikri::LDP
21
34
  end
22
35
 
23
36
  ##
24
- # Saves and forces reload. This updates the graph with any management
37
+ # Saves and forces reload. This updates the graph with any management
25
38
  # triples added by the LDP endpoint.
26
39
  #
27
40
  # @see #save
28
41
  def save_and_reload(*args)
29
42
  result = save(*args)
30
43
  get({}, true)
31
- result
44
+ result
32
45
  end
33
46
 
34
47
  ##
@@ -42,6 +55,12 @@ module Krikri::LDP
42
55
  result
43
56
  end
44
57
 
58
+ ##
59
+ # @return [self]
60
+ def rdf_source
61
+ self
62
+ end
63
+
45
64
  ##
46
65
  # Adds an appropritate provenance statement with the given URI and saves
47
66
  # the resource.
@@ -64,8 +83,7 @@ module Krikri::LDP
64
83
  # @see http://www.w3.org/TR/prov-primer/
65
84
  # @see http://www.w3.org/TR/2013/REC-prov-o-20130430/
66
85
  def save_with_provenance(activity_uri)
67
- predicate =
68
- exists? ? RDF::DPLA.wasRevisedBy : RDF::PROV.wasGeneratedBy
86
+ predicate = exists? ? REVISED_URI : GENERATED_URI
69
87
  self << RDF::Statement(self, predicate, activity_uri)
70
88
  save
71
89
  end
@@ -75,7 +93,7 @@ module Krikri::LDP
75
93
  ##
76
94
  # Clears the RDF::Graph and repopulates it from the http body. Forces text
77
95
  # encoding to UTF-8 before passing to the `RDF::Reader`.
78
- #
96
+ #
79
97
  # @return [void]
80
98
  #
81
99
  # @see http://www.w3.org/TR/turtle/#sec-mime for info about Turtle encoding
@@ -3,7 +3,28 @@ require 'faraday_middleware'
3
3
 
4
4
  module Krikri::LDP
5
5
  ##
6
- # Implements basic LDP CRUD operations
6
+ # Implements basic LDP CRUD operations. Requires an implementation of
7
+ # `#rdf_subject` returning an `RDF::URI`. The resource idenitified by the URI
8
+ # must conform to LDP Resource's interaction patterns.
9
+ #
10
+ # @example implementing a resource
11
+ # class MyResource
12
+ # include Krikri::LDP::Resource
13
+ #
14
+ # def rdf_subject
15
+ # @rdf_subject ||= RDF::URI('http://example.com/ldp/a/resource/path')
16
+ # end
17
+ # end
18
+ #
19
+ # @note Ideally, this is a general purpose LDP resource. However some HTTP
20
+ # PUT creation behavior may be specific to Marmotta. This avoids the need to
21
+ # interact directly with the owning container, but is a less generalized
22
+ # implementation.
23
+ #
24
+ # @see http://www.w3.org/TR/ldp/#h-ldpr-resource LDP Resource interaction
25
+ # patterns.
26
+ # @see https://wiki.apache.org/marmotta/LDPImplementationReport for
27
+ # information about Marmotta's PUT.
7
28
  module Resource
8
29
  extend ActiveSupport::Concern
9
30
 
@@ -134,7 +134,6 @@ module Krikri
134
134
 
135
135
  set_value(sr, :relation, parent_sr.relation)
136
136
  set_value(sr, :rights, parent_sr.rights, true)
137
- set_value(sr, :temporal, parent_sr.temporal)
138
137
  set_value(sr, :title, parent_sr.title)
139
138
 
140
139
  set_value(sr, :collection, parent_sr.collection) do |coll|
@@ -149,6 +148,10 @@ module Krikri
149
148
  build_time_span(date)
150
149
  end
151
150
 
151
+ set_value(sr, :temporal, parent_sr.temporal) do |temporal|
152
+ build_time_span(temporal)
153
+ end
154
+
152
155
  set_value(sr, :spatial, parent_sr.spatial) do |place|
153
156
  build_place(place)
154
157
  end
@@ -173,7 +176,7 @@ module Krikri
173
176
  def build_time_span(source)
174
177
  return unless source.is_a? DPLA::MAP::TimeSpan
175
178
  date = {}
176
- date[:displayDate]
179
+ set_value(date, :displayDate, source.prefLabel, true, &:as_json)
177
180
  set_value(date, :begin, source.begin, true, &:as_json)
178
181
  set_value(date, :end, source.end, true, &:as_json)
179
182
 
@@ -1,3 +1,3 @@
1
1
  module Krikri
2
- VERSION = '0.10.1'.freeze
2
+ VERSION = '0.11.0'.freeze
3
3
  end
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: /home/tjohnson/src/dpla/krikri
3
3
  specs:
4
- krikri (0.10.0)
4
+ krikri (0.10.1)
5
5
  analysand (= 4.0.0)
6
6
  audumbla (~> 0.1)
7
7
  blacklight (~> 5.8.0)
@@ -152,8 +152,8 @@ GEM
152
152
  domain_name (~> 0.5)
153
153
  http_parser.rb (0.6.0)
154
154
  i18n (0.7.0)
155
- jbuilder (2.3.2)
156
- activesupport (>= 3.0.0, < 5)
155
+ jbuilder (2.4.0)
156
+ activesupport (>= 3.0.0, < 5.1)
157
157
  multi_json (~> 1.2)
158
158
  jettywrapper (2.0.3)
159
159
  activesupport (>= 3.0.0)
@@ -242,7 +242,7 @@ GEM
242
242
  activesupport (= 4.1.6)
243
243
  rake (>= 0.8.7)
244
244
  thor (>= 0.18.1, < 2.0)
245
- rake (10.4.2)
245
+ rake (10.5.0)
246
246
  rdf (1.1.17.1)
247
247
  link_header (~> 0.0, >= 0.0.8)
248
248
  rdf-aggregate-repo (1.1.0.1)