krikri 0.10.1 → 0.11.0

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