valkyrie 2.2.0 → 3.0.0.pre.beta.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.circleci/config.yml +12 -2
- data/.lando.yml +1 -1
- data/.rubocop.yml +7 -0
- data/CHANGELOG.md +39 -13
- data/CONTRIBUTING.md +30 -8
- data/README.md +5 -1
- data/lib/valkyrie/id.rb +1 -16
- data/lib/valkyrie/persistence/buffered_persister.rb +2 -2
- data/lib/valkyrie/persistence/composite_persister.rb +3 -3
- data/lib/valkyrie/persistence/custom_query_container.rb +8 -16
- data/lib/valkyrie/persistence/fedora/persister/model_converter.rb +0 -1
- data/lib/valkyrie/persistence/fedora/persister/orm_converter.rb +1 -26
- data/lib/valkyrie/persistence/fedora/persister.rb +3 -1
- data/lib/valkyrie/persistence/memory/persister.rb +18 -2
- data/lib/valkyrie/persistence/postgres/persister.rb +4 -1
- data/lib/valkyrie/persistence/solr/metadata_adapter.rb +15 -3
- data/lib/valkyrie/persistence/solr/model_converter.rb +8 -25
- data/lib/valkyrie/persistence/solr/orm_converter.rb +1 -1
- data/lib/valkyrie/persistence/solr/persister.rb +16 -4
- data/lib/valkyrie/persistence/solr/repository.rb +17 -7
- data/lib/valkyrie/resource/access_controls.rb +1 -1
- data/lib/valkyrie/resource.rb +0 -1
- data/lib/valkyrie/specs/shared_specs/file.rb +1 -0
- data/lib/valkyrie/specs/shared_specs/persister.rb +20 -2
- data/lib/valkyrie/specs/shared_specs/queries.rb +7 -0
- data/lib/valkyrie/specs/shared_specs/storage_adapter.rb +19 -0
- data/lib/valkyrie/specs/shared_specs/write_only/metadata_adapter.rb +62 -0
- data/lib/valkyrie/specs/shared_specs.rb +2 -0
- data/lib/valkyrie/storage/disk.rb +24 -1
- data/lib/valkyrie/storage_adapter.rb +1 -1
- data/lib/valkyrie/version.rb +1 -1
- data/lib/valkyrie.rb +0 -14
- data/valkyrie.gemspec +0 -2
- metadata +5 -32
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 240eb74a4be15202ae2036ac9a0067b1aadc23e65ad583638b6cd2874540a845
|
4
|
+
data.tar.gz: 65568d10c48dcadbee70bff7c23360a6e0b149f874b5ebe493b5770afb6e4362
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 29b4ebfb067823191dfb2ca74151765ad8157f2c4b7b89cbecaf882e8e6f32eded31fdfb326ca198192e50574a562f8582205bab921d248707f2993b31960528
|
7
|
+
data.tar.gz: 1fc174038d3baac66d35e431211ed83e38d2531395f86ecfeee106a612631d42a1f2fa11fe55a561617d3a23561763707100c701a73a06147db11bb073a60636
|
data/.circleci/config.yml
CHANGED
@@ -28,7 +28,7 @@ jobs:
|
|
28
28
|
environment:
|
29
29
|
CATALINA_OPTS: "-Djava.awt.headless=true -Dfile.encoding=UTF-8 -server -Xms512m -Xmx1024m -XX:NewSize=256m -XX:MaxNewSize=256m -XX:PermSize=256m -XX:MaxPermSize=256m -XX:+DisableExplicitGC"
|
30
30
|
JAVA_OPTIONS: "-Djetty.http.port=8998 -Dfcrepo.dynamic.jms.port=61618 -Dfcrepo.dynamic.stomp.port=61614"
|
31
|
-
- image: fcrepo/fcrepo:6.0.0
|
31
|
+
- image: fcrepo/fcrepo:6.0.0
|
32
32
|
environment:
|
33
33
|
CATALINA_OPTS: "-Djava.awt.headless=true -Dfile.encoding=UTF-8 -server -Xms512m -Xmx1024m -XX:NewSize=256m -XX:MaxNewSize=256m -XX:PermSize=256m -XX:MaxPermSize=256m -XX:+DisableExplicitGC -Dorg.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH=true"
|
34
34
|
JAVA_OPTS: "-Djetty.http.port=8978 -Dfcrepo.dynamic.jms.port=61619 -Dfcrepo.dynamic.stomp.port=61615 -Dorg.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH=true"
|
@@ -40,6 +40,16 @@ jobs:
|
|
40
40
|
FEDORA_6_PORT: 8080
|
41
41
|
steps:
|
42
42
|
- checkout
|
43
|
+
|
44
|
+
- run:
|
45
|
+
name: Check for 'master' branch
|
46
|
+
command: |
|
47
|
+
git fetch --all --quiet --prune --prune-tags
|
48
|
+
if [[ -n "$(git branch --all --list master */master)" ]]; then
|
49
|
+
echo "A branch named 'master' was found. Please remove it."
|
50
|
+
echo "$(git branch --all --list master */master)"
|
51
|
+
fi
|
52
|
+
[[ -z "$(git branch --all --list master */master)" ]]
|
43
53
|
- run:
|
44
54
|
name: Wait for solr
|
45
55
|
command: dockerize -wait tcp://localhost:8994 -timeout 1m
|
@@ -55,7 +65,7 @@ jobs:
|
|
55
65
|
- restore_cache:
|
56
66
|
keys:
|
57
67
|
- bundle-{{ checksum "<< parameters.gemfile >>" }}-{{ checksum "valkyrie.gemspec" }}-<< parameters.ruby >>-6
|
58
|
-
- run: sudo apt
|
68
|
+
- run: sudo apt update -y && sudo apt-get install -y libpq-dev lsof
|
59
69
|
- run:
|
60
70
|
name: Set BUNDLE_GEMFILE
|
61
71
|
command: |
|
data/.lando.yml
CHANGED
data/.rubocop.yml
CHANGED
@@ -39,3 +39,10 @@ Naming/FileName:
|
|
39
39
|
Exclude:
|
40
40
|
- 'Appraisals'
|
41
41
|
- 'Gemfile'
|
42
|
+
Metrics/MethodLength:
|
43
|
+
Exclude:
|
44
|
+
- 'lib/valkyrie/persistence/postgres/persister.rb'
|
45
|
+
- 'lib/valkyrie/persistence/fedora/persister.rb'
|
46
|
+
Metrics/CyclomaticComplexity:
|
47
|
+
Exclude:
|
48
|
+
- 'lib/valkyrie/persistence/postgres/persister.rb'
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,29 @@
|
|
1
|
+
# v3.0.0.beta1 2021-10-18
|
2
|
+
|
3
|
+
## Changes since last release
|
4
|
+
|
5
|
+
* More informative NoMethodError for missing custom queries.
|
6
|
+
[no-reply](https://github.com/no-reply)
|
7
|
+
* Persisters error if you save something which existed at query time, but has
|
8
|
+
since been deleted.
|
9
|
+
* Shared write-only persisters.
|
10
|
+
* Solr ModelConverter performance.
|
11
|
+
* Don't hold open a file handle from StorageAdapter#upload/find_by
|
12
|
+
* Files can be #close'd.
|
13
|
+
* Remove Draper
|
14
|
+
* Remove deprecated string equality function.
|
15
|
+
* Remove deprecated BlacklistedValue.
|
16
|
+
* Allow reform upgrade to 2.6.
|
17
|
+
* Don't require a specific error message in shared specs
|
18
|
+
[tpendragon](https://github.com/tpendragon)
|
19
|
+
|
20
|
+
Additional thanks to the following for code review and issue reports leading to
|
21
|
+
this release:
|
22
|
+
|
23
|
+
[hackartisan](https://github.com/hackartisan)
|
24
|
+
[eliotjordan](https://github.com/eliotjordan)
|
25
|
+
|
26
|
+
|
1
27
|
# v2.2.0 2021-05-06
|
2
28
|
|
3
29
|
## Changes since last release
|
@@ -60,9 +86,9 @@ this release:
|
|
60
86
|
* Use `::` prefixed names for `JSON::LD` references.
|
61
87
|
[no-reply](https://github.com/no-reply)
|
62
88
|
* Use SVG instead of PNG for version badge.
|
63
|
-
[
|
89
|
+
[hackartisan](https://github.com/hackartisan)
|
64
90
|
* Fix Rubocop for latest Bixby.
|
65
|
-
[
|
91
|
+
[hackartisan](https://github.com/hackartisan)
|
66
92
|
* Valkyrie IDs equal string equivalent of ID to String with config
|
67
93
|
[jlevnhv](https://github.com/jlevnhv)
|
68
94
|
* Add optional model parameter to find_inverse_references_by and
|
@@ -207,7 +233,7 @@ involved for their contributions in the last year:
|
|
207
233
|
[cjcolvar](https://github.com/cjcolvar)
|
208
234
|
[dgcliff](https://github.com/dgcliff)
|
209
235
|
[escowles](https://github.com/escowles)
|
210
|
-
[
|
236
|
+
[hackartisan](https://github.com/hackartisan)
|
211
237
|
[jeremyf](https://github.com/jeremyf)
|
212
238
|
[jrgriffiniii](https://github.com/jrgriffiniii)
|
213
239
|
[kelynch](https://github.com/kelynch)
|
@@ -288,17 +314,17 @@ this release:
|
|
288
314
|
* Provide a warning when postgres adapter overwrites an ID, deprecate this
|
289
315
|
behavior so it will throw an exception in the future.
|
290
316
|
[cam156](https://github.com/cam156)
|
291
|
-
[
|
317
|
+
[hackartisan](https://github.com/hackartisan)
|
292
318
|
[tpendragon](https://github.com/tpendragon)
|
293
319
|
* Add support for passing just an ID to find_inverse_references_by
|
294
320
|
[cam156](https://github.com/cam156)
|
295
|
-
[
|
321
|
+
[hackartisan](https://github.com/hackartisan)
|
296
322
|
* Fix memory adapter raising an exception in find_by_alternate_identifier when
|
297
323
|
there are resources without the alternate_identifier attribute.
|
298
324
|
[jeremyf](https://github.com/jeremyf)
|
299
325
|
* Provide a warning when using the postgres adapter without manually providing
|
300
326
|
the pg gem, so it can be an optional dependency in 2.0.0.
|
301
|
-
[
|
327
|
+
[hackartisan](https://github.com/hackartisan)
|
302
328
|
* Provide guidance in specs on how to define alternate_ids
|
303
329
|
[cjcolvar](https://github.com/cjcolvar)
|
304
330
|
* Upload files to Fedora using form/multipart.
|
@@ -344,17 +370,17 @@ Additional thanks to the following for code review:
|
|
344
370
|
* Provide a warning when postgres adapter overwrites an ID, deprecate this
|
345
371
|
behavior so it will throw an exception in the future.
|
346
372
|
[cam156](https://github.com/cam156)
|
347
|
-
[
|
373
|
+
[hackartisan](https://github.com/hackartisan)
|
348
374
|
[tpendragon](https://github.com/tpendragon)
|
349
375
|
* Add support for passing just an ID to find_inverse_references_by
|
350
376
|
[cam156](https://github.com/cam156)
|
351
|
-
[
|
377
|
+
[hackartisan](https://github.com/hackartisan)
|
352
378
|
* Fix memory adapter raising an exception in find_by_alternate_identifier when
|
353
379
|
there are resources without the alternate_identifier attribute.
|
354
380
|
[jeremyf](https://github.com/jeremyf)
|
355
381
|
* Provide a warning when using the postgres adapter without manually providing
|
356
382
|
the pg gem, so it can be an optional dependency in 2.0.0.
|
357
|
-
[
|
383
|
+
[hackartisan](https://github.com/hackartisan)
|
358
384
|
* Provide guidance in specs on how to define alternate_ids
|
359
385
|
[cjcolvar](https://github.com/cjcolvar)
|
360
386
|
* Upload files to Fedora using form/multipart.
|
@@ -405,7 +431,7 @@ this release:
|
|
405
431
|
## Changes since last release
|
406
432
|
|
407
433
|
* Fix solr persister to pass through exceptions on timeout
|
408
|
-
[
|
434
|
+
[hackartisan](https://github.com/hackartisan)
|
409
435
|
* Fix generated specs to work with shared_specs expectation
|
410
436
|
[revgum](https://github.com/revgum)
|
411
437
|
|
@@ -452,7 +478,7 @@ following participants who made it happen:
|
|
452
478
|
* [cam156](https://github.com/cam156)
|
453
479
|
* [DanCoughlin](https://github.com/DanCoughlin)
|
454
480
|
* [escowles](https://github.com/escowles)
|
455
|
-
* [
|
481
|
+
* [hackartisan](https://github.com/hackartisan)
|
456
482
|
* [jrgriffiniii](https://github.com/jrgriffiniii)
|
457
483
|
* [mtribone](https://github.com/mtribone)
|
458
484
|
* [tpendragon](https://github.com/tpendragon)
|
@@ -469,7 +495,7 @@ following participants who made it happen:
|
|
469
495
|
## Changes since last release
|
470
496
|
|
471
497
|
* Loosened ActiveRecord restriction to allow upgrading pg gem to 1.0.
|
472
|
-
[
|
498
|
+
[hackartisan](https://github.com/hackartisan)
|
473
499
|
|
474
500
|
# v1.1.0 2018-05-08
|
475
501
|
|
@@ -548,7 +574,7 @@ Initial Release
|
|
548
574
|
* [dlpierce](https://github.com/dlpierce)
|
549
575
|
* [escowles](https://github.com/escowles)
|
550
576
|
* [geekscruff](https://github.com/geekscruff)
|
551
|
-
* [
|
577
|
+
* [hackartisan](https://github.com/hackartisan)
|
552
578
|
* [jcoyne](https://github.com/jcoyne)
|
553
579
|
* [jeremyf](https://github.com/jeremyf)
|
554
580
|
* [jgondron](https://github.com/jgondron)
|
data/CONTRIBUTING.md
CHANGED
@@ -22,6 +22,28 @@ https://wiki.duraspace.org/display/samvera/Samvera+Community+Intellectual+Proper
|
|
22
22
|
|
23
23
|
You should also add yourself to the `CONTRIBUTORS.md` file in the root of the project.
|
24
24
|
|
25
|
+
## Language
|
26
|
+
The language we use matters. Today, tomorrow, and for years to come
|
27
|
+
people will read the code we write. They will judge us for our
|
28
|
+
design, logic, and the words we use to describe the system.
|
29
|
+
Our words should be accessible. Favor descriptive words that give
|
30
|
+
meaning while avoiding reinforcing systemic inequities. For example,
|
31
|
+
in the Samvera community, we should favor using allowed\_list instead
|
32
|
+
of whitelist, denied\_list instead of blacklist, or source/copy
|
33
|
+
instead of master/slave.
|
34
|
+
We're going to get it wrong, but this is a call to keep working to
|
35
|
+
make it right. View our code and the words we choose as a chance to
|
36
|
+
have a conversation. A chance to grow an understanding of the systems
|
37
|
+
we develop as well as the systems in which we live.
|
38
|
+
See [“Blacklists” and “whitelists”: a salutary warning concerning the
|
39
|
+
prevalence of racist language in discussions of predatory
|
40
|
+
publishing](https://www.ncbi.nlm.nih.gov/pmc/articles/PMC6148600/) for
|
41
|
+
further details.
|
42
|
+
|
43
|
+
If you're working on PR for this project, create a feature branch off of `main`.
|
44
|
+
|
45
|
+
Please ***do not*** create a branch called `master` for this repository or as part of your pull request; the branch will either need to be removed or renamed before it can be considered for inclusion in the code base and history of our repositories.
|
46
|
+
|
25
47
|
## Contribution Tasks
|
26
48
|
|
27
49
|
* Reporting Issues
|
@@ -45,10 +67,10 @@ You should also add yourself to the `CONTRIBUTORS.md` file in the root of the pr
|
|
45
67
|
|
46
68
|
* Fork the repository on GitHub
|
47
69
|
* Create a topic branch from where you want to base your work.
|
48
|
-
* This is usually the
|
49
|
-
* To quickly create a topic branch based on
|
50
|
-
* Then checkout the new branch with `git checkout fix/
|
51
|
-
* Please avoid working directly on the `
|
70
|
+
* This is usually the main branch.
|
71
|
+
* To quickly create a topic branch based on main; `git branch fix/main/my_contribution main`
|
72
|
+
* Then checkout the new branch with `git checkout fix/main/my_contribution`.
|
73
|
+
* Please avoid working directly on the `main` branch.
|
52
74
|
* You may find the [hub suite of commands](https://github.com/defunkt/hub) helpful
|
53
75
|
* Make sure you have added sufficient tests and documentation for your changes.
|
54
76
|
* Test functionality with RSpec; Test features / UI with Capybara.
|
@@ -109,15 +131,15 @@ You should also add yourself to the `CONTRIBUTORS.md` file in the root of the pr
|
|
109
131
|
### Submitting Changes
|
110
132
|
|
111
133
|
* Read the article ["Using Pull Requests"](https://help.github.com/articles/using-pull-requests) on GitHub.
|
112
|
-
* Make sure your branch is up to date with its parent branch (i.e.
|
113
|
-
* `git checkout
|
134
|
+
* Make sure your branch is up to date with its parent branch (i.e. main)
|
135
|
+
* `git checkout main`
|
114
136
|
* `git pull --rebase`
|
115
137
|
* `git checkout <your-branch>`
|
116
|
-
* `git rebase
|
138
|
+
* `git rebase main`
|
117
139
|
* It is a good idea to run your tests again.
|
118
140
|
* If you've made more than one commit take a moment to consider whether squashing commits together would help improve their logical grouping.
|
119
141
|
* [Detailed Walkthrough of One Pull Request per Commit](http://ndlib.github.io/practices/one-commit-per-pull-request/)
|
120
|
-
* `git rebase --interactive
|
142
|
+
* `git rebase --interactive main` ([See Github help](https://help.github.com/articles/interactive-rebase))
|
121
143
|
* Squashing your branch's changes into one commit is "good form" and helps the person merging your request to see everything that is going on.
|
122
144
|
* Push your changes to a topic branch in your fork of the repository.
|
123
145
|
* Submit a pull request from your fork to the project.
|
data/README.md
CHANGED
@@ -239,7 +239,7 @@ custom adapters, but your application will still work.
|
|
239
239
|
Using the shared specs in your own models is described in more detail on the [Shared Specs Wiki
|
240
240
|
page](https://github.com/samvera/valkyrie/wiki/Shared-Specs).
|
241
241
|
|
242
|
-
### Fedora 5 Compatibility
|
242
|
+
### Fedora 5/6 Compatibility
|
243
243
|
When configuring your adapter, include the `fedora_version` parameter in your metadata or storage adapter
|
244
244
|
config. If Fedora requires auth, you can also include that in the URL, e.g.:
|
245
245
|
|
@@ -282,3 +282,7 @@ This software has been developed by and is brought to you by the Samvera communi
|
|
282
282
|
## Contributing
|
283
283
|
|
284
284
|
Bug reports and pull requests are welcome on GitHub at https://github.com/samvera/valkyrie/.
|
285
|
+
|
286
|
+
If you're working on PR for this project, create a feature branch off of `main`.
|
287
|
+
|
288
|
+
This repository follows the [Samvera Community Code of Conduct](https://samvera.atlassian.net/wiki/spaces/samvera/pages/405212316/Code+of+Conduct) and [language recommendations](https://github.com/samvera/maintenance/blob/master/templates/CONTRIBUTING.md#language). Please ***do not*** create a branch called `master` for this repository or as part of your pull request; the branch will either need to be removed or renamed before it can be considered for inclusion in the code base and history of this repository.
|
data/lib/valkyrie/id.rb
CHANGED
@@ -25,27 +25,12 @@ module Valkyrie
|
|
25
25
|
delegate :hash, to: :state
|
26
26
|
|
27
27
|
def eql?(other)
|
28
|
-
|
29
|
-
default_equality(other)
|
28
|
+
other == to_str
|
30
29
|
end
|
31
30
|
alias == eql?
|
32
31
|
|
33
32
|
protected
|
34
33
|
|
35
|
-
def default_equality(other)
|
36
|
-
output = (other.class == self.class && other.state == state)
|
37
|
-
return output if output == true
|
38
|
-
if output == false && string_equality(other) && Valkyrie.config.id_string_equality.nil?
|
39
|
-
warn "[DEPRECATION] Valkyrie::IDs will always be equal to their string counterparts in 3.0.0. " \
|
40
|
-
"To silence this message, please either compare IDs or set Valkyrie.config.id_string_equality = true."
|
41
|
-
end
|
42
|
-
false
|
43
|
-
end
|
44
|
-
|
45
|
-
def string_equality(other)
|
46
|
-
other == to_str
|
47
|
-
end
|
48
|
-
|
49
34
|
def state
|
50
35
|
[@id]
|
51
36
|
end
|
@@ -24,8 +24,8 @@ module Valkyrie::Persistence
|
|
24
24
|
@buffer_class = buffer_class
|
25
25
|
end
|
26
26
|
|
27
|
-
def save(resource:)
|
28
|
-
persister.save(resource: resource)
|
27
|
+
def save(resource:, external_resource: false)
|
28
|
+
persister.save(resource: resource, external_resource: external_resource)
|
29
29
|
end
|
30
30
|
|
31
31
|
def save_all(resources:)
|
@@ -24,14 +24,14 @@ module Valkyrie::Persistence
|
|
24
24
|
end
|
25
25
|
|
26
26
|
# (see Valkyrie::Persistence::Memory::Persister#save)
|
27
|
-
def save(resource:)
|
27
|
+
def save(resource:, external_resource: false)
|
28
28
|
# Assume the first persister is the canonical data store; that's the optlock we want
|
29
29
|
first, *rest = *persisters
|
30
|
-
cached_resource = first.save(resource: resource)
|
30
|
+
cached_resource = first.save(resource: resource, external_resource: external_resource)
|
31
31
|
# Don't pass opt lock tokens to other persisters
|
32
32
|
internal_resource = cached_resource.dup
|
33
33
|
internal_resource.clear_optimistic_lock_token!
|
34
|
-
rest.inject(internal_resource) { |m, persister| persister.save(resource: m) }
|
34
|
+
rest.inject(internal_resource) { |m, persister| persister.save(resource: m, external_resource: true) }
|
35
35
|
# return the one with the desired opt lock token
|
36
36
|
cached_resource
|
37
37
|
end
|
@@ -39,25 +39,17 @@ module Valkyrie::Persistence
|
|
39
39
|
attr_reader :query_service, :query_handlers
|
40
40
|
def initialize(query_service:)
|
41
41
|
@query_service = query_service
|
42
|
-
@query_handlers =
|
42
|
+
@query_handlers = {}
|
43
43
|
end
|
44
44
|
|
45
45
|
def register_query_handler(query_handler)
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
end
|
54
|
-
|
55
|
-
def find_query_handler(method)
|
56
|
-
query_handlers.find { |x| x.queries.include?(method) }
|
57
|
-
end
|
58
|
-
|
59
|
-
def respond_to_missing?(meth_name, _args)
|
60
|
-
find_query_handler(meth_name).present?
|
46
|
+
query_handler.queries.each do |query|
|
47
|
+
handler = query_handler.new(query_service: query_service)
|
48
|
+
query_handlers[query.to_sym] = handler
|
49
|
+
define_singleton_method query do |*args, &block|
|
50
|
+
query_handlers[query.to_sym].__send__(query, *args, &block)
|
51
|
+
end
|
52
|
+
end
|
61
53
|
end
|
62
54
|
end
|
63
55
|
end
|
@@ -313,7 +313,6 @@ module Valkyrie::Persistence::Fedora
|
|
313
313
|
end
|
314
314
|
|
315
315
|
# Generate a new RDF hash URI for the "child" graph for the ModelConverter::Property
|
316
|
-
# @see https://github.com/fcrepo4/fcrepo4/blob/master/fcrepo-kernel-modeshape/src/main/java/org/fcrepo/kernel/modeshape/rdf/JcrRdfTools.java#L455
|
317
316
|
# @return [RDF::Graph]
|
318
317
|
def subject_uri
|
319
318
|
@subject_uri ||= ::RDF::URI(RDF::Node.new.to_s.gsub("_:", "#"))
|
@@ -99,6 +99,7 @@ module Valkyrie::Persistence::Fedora
|
|
99
99
|
|
100
100
|
# Class for handling cases where deny listed values should not be mapped
|
101
101
|
class DenylistedValue < ::Valkyrie::ValueMapper
|
102
|
+
FedoraValue.register(self)
|
102
103
|
# Determines whether or not the value has a denied namespace for the RDF statement object
|
103
104
|
# (i. e. avoid attempting to map any RDF statements making assertions about LDP containers or resource internal to Fedora)
|
104
105
|
# @param [Property] value
|
@@ -113,14 +114,6 @@ module Valkyrie::Persistence::Fedora
|
|
113
114
|
end
|
114
115
|
end
|
115
116
|
|
116
|
-
# @deprecated
|
117
|
-
# Class for handling cases where deny listed values should not be mapped
|
118
|
-
# @see DenylistedValue
|
119
|
-
class BlacklistedValue < DenylistedValue
|
120
|
-
FedoraValue.register(self)
|
121
|
-
warn "[DEPRECATION] Samvera is deprecating '#{self}' in 3.0.0. Use #{DenylistedValue} instead."
|
122
|
-
end
|
123
|
-
|
124
117
|
# Class for handling cases where the RDF subject of a Property references a separate resource using a hash URI
|
125
118
|
class DifferentSubject < ::Valkyrie::ValueMapper
|
126
119
|
FedoraValue.register(self)
|
@@ -547,16 +540,6 @@ module Valkyrie::Persistence::Fedora
|
|
547
540
|
key
|
548
541
|
end
|
549
542
|
|
550
|
-
# @deprecated
|
551
|
-
# Determines whether or not a key is on the deny list for mapping
|
552
|
-
# (For example <http://fedora.info/definitions> assertions are not mapped to Valkyrie attributes)
|
553
|
-
# @param [Symbol] key
|
554
|
-
# @return [Boolean]
|
555
|
-
def blacklist?(key)
|
556
|
-
warn "[DEPRECATION] Samvera is deprecating '#{self.class}#blacklist?' in 3.0.0. Use #{self.class}#deny? instead."
|
557
|
-
deny?(key)
|
558
|
-
end
|
559
|
-
|
560
543
|
# Determines whether or not a key is on the deny list for mapping
|
561
544
|
# (For example <http://fedora.info/definitions> assertions are not mapped to Valkyrie attributes)
|
562
545
|
# @param [Symbol] key
|
@@ -575,14 +558,6 @@ module Valkyrie::Persistence::Fedora
|
|
575
558
|
Array(values)
|
576
559
|
end
|
577
560
|
|
578
|
-
# @deprecated
|
579
|
-
# Retrieve a list of denied URIs for predicates
|
580
|
-
# @return [Array<String>]
|
581
|
-
def blacklist
|
582
|
-
warn "[DEPRECATION] Samvera is deprecating '#{self.class}#blacklist' in 3.0.0. Use #{self.class}#denylist instead."
|
583
|
-
denylist
|
584
|
-
end
|
585
|
-
|
586
561
|
# Retrieve a list of denied URIs for predicates
|
587
562
|
# @return [Array<String>]
|
588
563
|
def denylist
|
@@ -13,7 +13,7 @@ module Valkyrie::Persistence::Fedora
|
|
13
13
|
end
|
14
14
|
|
15
15
|
# (see Valkyrie::Persistence::Memory::Persister#save)
|
16
|
-
def save(resource:)
|
16
|
+
def save(resource:, external_resource: false)
|
17
17
|
initialize_repository
|
18
18
|
internal_resource = resource.dup
|
19
19
|
internal_resource.created_at ||= Time.current
|
@@ -35,6 +35,8 @@ module Valkyrie::Persistence::Fedora
|
|
35
35
|
alternate_resources ? save_reference_to_resource(persisted_resource, alternate_resources) : persisted_resource
|
36
36
|
rescue Ldp::PreconditionFailed
|
37
37
|
raise Valkyrie::Persistence::StaleObjectError, "The object #{internal_resource.id} has been updated by another process."
|
38
|
+
rescue Ldp::Gone
|
39
|
+
raise Valkyrie::Persistence::ObjectNotFoundError, "The object #{resource.id} is previously persisted but not found at save time."
|
38
40
|
end
|
39
41
|
|
40
42
|
# (see Valkyrie::Persistence::Memory::Persister#save_all)
|
@@ -5,7 +5,7 @@ module Valkyrie::Persistence::Memory
|
|
5
5
|
# @note Documentation for persisters in general is maintained here.
|
6
6
|
class Persister
|
7
7
|
attr_reader :adapter
|
8
|
-
delegate :cache, to: :adapter
|
8
|
+
delegate :cache, :query_service, to: :adapter
|
9
9
|
|
10
10
|
# @param adapter [Valkyrie::Persistence::Memory::MetadataAdapter] The memory adapter which
|
11
11
|
# holds the cache for this persister.
|
@@ -16,11 +16,18 @@ module Valkyrie::Persistence::Memory
|
|
16
16
|
|
17
17
|
# Save a single resource.
|
18
18
|
# @param resource [Valkyrie::Resource] The resource to save.
|
19
|
+
# @param external_resource [Boolean] Whether the resource to be saved comes
|
20
|
+
# from a different metadata store. Allows a resource to be saved even if
|
21
|
+
# it's not already in the store. For example, if you're indexing a
|
22
|
+
# resource into Solr - it's saved in your primary metadata store, but not
|
23
|
+
# in Solr, so it's okay if it doesn't exist in Solr but is marked as
|
24
|
+
# persisted.
|
19
25
|
# @return [Valkyrie::Resource] The resource with an `#id` value generated by the
|
20
26
|
# persistence backend.
|
21
27
|
# @raise [Valkyrie::Persistence::StaleObjectError]
|
22
|
-
def save(resource:)
|
28
|
+
def save(resource:, external_resource: false)
|
23
29
|
raise Valkyrie::Persistence::StaleObjectError, "The object #{resource.id} has been updated by another process." unless valid_lock?(resource)
|
30
|
+
raise Valkyrie::Persistence::ObjectNotFoundError, "The object #{resource.id} is previously persisted but not found at save time." unless external_resource || valid_for_save?(resource)
|
24
31
|
|
25
32
|
# duplicate the resource so we are not creating side effects on the caller's resource
|
26
33
|
internal_resource = resource.dup
|
@@ -34,6 +41,15 @@ module Valkyrie::Persistence::Memory
|
|
34
41
|
cache[internal_resource.id] = internal_resource
|
35
42
|
end
|
36
43
|
|
44
|
+
# return true if resource is
|
45
|
+
# persisted and found
|
46
|
+
# or
|
47
|
+
# not persisted
|
48
|
+
def valid_for_save?(resource)
|
49
|
+
return true unless resource.persisted? # a new resource
|
50
|
+
query_service.find_by(id: resource.id).present? # a persisted resource must be found
|
51
|
+
end
|
52
|
+
|
37
53
|
# Save a batch of resources.
|
38
54
|
# @param resources [Array<Valkyrie::Resource>] List of resources to save.
|
39
55
|
# @return [Array<Valkyrie::Resource>] List of resources with an `#id` value
|
@@ -17,9 +17,12 @@ module Valkyrie::Persistence::Postgres
|
|
17
17
|
# @return [Valkyrie::Resource] the persisted/updated resource
|
18
18
|
# @raise [Valkyrie::Persistence::StaleObjectError] raised if the resource
|
19
19
|
# was modified in the database between been read into memory and persisted
|
20
|
-
def save(resource:)
|
20
|
+
def save(resource:, external_resource: false)
|
21
21
|
orm_object = resource_factory.from_resource(resource: resource)
|
22
22
|
orm_object.transaction do
|
23
|
+
if !external_resource && resource.persisted? && !orm_object.persisted?
|
24
|
+
raise Valkyrie::Persistence::ObjectNotFoundError, "The object #{resource.id} is previously persisted but not found at save time."
|
25
|
+
end
|
23
26
|
orm_object.save!
|
24
27
|
if resource.id && resource.id.to_s != orm_object.id
|
25
28
|
raise Valkyrie::Persistence::UnsupportedDatatype,
|
@@ -27,18 +27,30 @@ module Valkyrie::Persistence::Solr
|
|
27
27
|
# )
|
28
28
|
# )
|
29
29
|
class MetadataAdapter
|
30
|
-
attr_reader :connection, :resource_indexer
|
30
|
+
attr_reader :connection, :resource_indexer, :write_only
|
31
31
|
# @param connection [RSolr::Client] The RSolr connection to index to.
|
32
32
|
# @param resource_indexer [Class, #to_solr] An indexer which is able to
|
33
33
|
# receive a `resource` argument and then has an instance method `#to_solr`
|
34
|
-
|
34
|
+
# @param write_only [Boolean] If true act as a write only adapter.
|
35
|
+
# @param soft_commit [Boolean] If false, don't soft commit.
|
36
|
+
def initialize(connection:, resource_indexer: NullIndexer, write_only: false, soft_commit: true)
|
35
37
|
@connection = connection
|
36
38
|
@resource_indexer = resource_indexer
|
39
|
+
@write_only = write_only
|
40
|
+
@soft_commit = soft_commit
|
37
41
|
end
|
38
42
|
|
39
43
|
# @return [Valkyrie::Persistence::Solr::Persister] The solr persister.
|
40
44
|
def persister
|
41
|
-
Valkyrie::Persistence::Solr::Persister.new(adapter: self)
|
45
|
+
@persister ||= Valkyrie::Persistence::Solr::Persister.new(adapter: self)
|
46
|
+
end
|
47
|
+
|
48
|
+
def write_only?
|
49
|
+
write_only
|
50
|
+
end
|
51
|
+
|
52
|
+
def soft_commit?
|
53
|
+
@soft_commit
|
42
54
|
end
|
43
55
|
|
44
56
|
# @return [Valkyrie::Persistence::Solr::QueryService] The solr query
|
@@ -82,7 +82,7 @@ module Valkyrie::Persistence::Solr
|
|
82
82
|
|
83
83
|
# Determines whether or not a field is multivalued
|
84
84
|
# @note this is tied to conventions in the Solr Schema
|
85
|
-
# @see https://github.com/samvera-labs/valkyrie/blob/
|
85
|
+
# @see https://github.com/samvera-labs/valkyrie/blob/main/solr/config/schema.xml
|
86
86
|
# @see https://lucene.apache.org/solr/guide/defining-fields.html#defining-fields
|
87
87
|
# @param [String] field
|
88
88
|
# @return [Boolean]
|
@@ -114,6 +114,7 @@ module Valkyrie::Persistence::Solr
|
|
114
114
|
properties.each_with_object({}) do |property, hsh|
|
115
115
|
next if property == Valkyrie::Persistence::Attributes::OPTIMISTIC_LOCK
|
116
116
|
attr = resource_attributes[property]
|
117
|
+
next if attr.nil?
|
117
118
|
mapper_val = SolrMapperValue.for(Property.new(property, attr)).result
|
118
119
|
unless mapper_val.respond_to?(:apply_to)
|
119
120
|
raise "Unable to cast #{resource_attributes[:internal_resource]}#" \
|
@@ -162,8 +163,8 @@ module Valkyrie::Persistence::Solr
|
|
162
163
|
# @param values [Array] Values to index into the given fields.
|
163
164
|
def initialize(key:, fields:, values:)
|
164
165
|
@key = key
|
165
|
-
@fields =
|
166
|
-
@values =
|
166
|
+
@fields = fields
|
167
|
+
@values = values
|
167
168
|
end
|
168
169
|
|
169
170
|
# @param hsh [Hash] The solr hash to apply to.
|
@@ -238,10 +239,10 @@ module Valkyrie::Persistence::Solr
|
|
238
239
|
# @note this prepends the string "serialized-" to the value indexed in Solr
|
239
240
|
# This is indexed as a stored multivalued text
|
240
241
|
# @see https://lucene.apache.org/solr/guide/defining-fields.html#defining-fields
|
241
|
-
# @see https://github.com/samvera-labs/valkyrie/blob/
|
242
|
+
# @see https://github.com/samvera-labs/valkyrie/blob/main/solr/config/schema.xml
|
242
243
|
# @return [SolrRow]
|
243
244
|
def result
|
244
|
-
SolrRow.new(key: value.key, fields: ["tsim"], values: "serialized-#{value.value.to_json}")
|
245
|
+
SolrRow.new(key: value.key, fields: ["tsim"], values: ["serialized-#{value.value.to_json}"])
|
245
246
|
end
|
246
247
|
end
|
247
248
|
|
@@ -267,24 +268,6 @@ module Valkyrie::Persistence::Solr
|
|
267
268
|
end
|
268
269
|
end
|
269
270
|
|
270
|
-
# Skips nil values.
|
271
|
-
class NilPropertyValue < ::Valkyrie::ValueMapper
|
272
|
-
SolrMapperValue.register(self)
|
273
|
-
|
274
|
-
# Determines whether or not a Property value responds as nil
|
275
|
-
# @param [Object] value
|
276
|
-
# @return [Boolean]
|
277
|
-
def self.handles?(value)
|
278
|
-
value.is_a?(Property) && value.value.nil?
|
279
|
-
end
|
280
|
-
|
281
|
-
# Constructs a SolrRow object for a Property with a nil value
|
282
|
-
# @return [SolrRow]
|
283
|
-
def result
|
284
|
-
SolrRow.new(key: value.key, fields: [], values: nil)
|
285
|
-
end
|
286
|
-
end
|
287
|
-
|
288
271
|
# Casts {Valkyrie::ID} values into a recognizable string in solr.
|
289
272
|
class IDPropertyValue < ::Valkyrie::ValueMapper
|
290
273
|
SolrMapperValue.register(self)
|
@@ -423,7 +406,7 @@ module Valkyrie::Persistence::Solr
|
|
423
406
|
# Constructs a SolrRow object with the String values and Solr field settings
|
424
407
|
# @return [SolrRow]
|
425
408
|
def result
|
426
|
-
SolrRow.new(key: value.key, fields: fields, values: value.value)
|
409
|
+
SolrRow.new(key: value.key, fields: fields, values: [value.value])
|
427
410
|
end
|
428
411
|
|
429
412
|
# Generates the Solr fields used during the indexing
|
@@ -436,7 +419,7 @@ module Valkyrie::Persistence::Solr
|
|
436
419
|
# - stored multivalued english text
|
437
420
|
# If the string is greater than 1000 characters in length, it is only indexed as a stored multivalued text
|
438
421
|
# @see https://lucene.apache.org/solr/guide/defining-fields.html#defining-fields
|
439
|
-
# @see https://github.com/samvera-labs/valkyrie/blob/
|
422
|
+
# @see https://github.com/samvera-labs/valkyrie/blob/main/solr/config/schema.xml
|
440
423
|
# @return [Array<Symbol>]
|
441
424
|
def fields
|
442
425
|
if value.value.length > 1000
|
@@ -79,7 +79,7 @@ module Valkyrie::Persistence::Solr
|
|
79
79
|
|
80
80
|
# Construct the Hash containing the Valkyrie Resource attributes using the Solr Document
|
81
81
|
# @note this filters for attributes which have been indexed as stored multivalued texts (tsim)
|
82
|
-
# @see https://github.com/samvera-labs/valkyrie/blob/
|
82
|
+
# @see https://github.com/samvera-labs/valkyrie/blob/main/solr/config/schema.xml
|
83
83
|
# @see https://lucene.apache.org/solr/guide/defining-fields.html#defining-fields
|
84
84
|
# @return [Hash]
|
85
85
|
def attribute_hash
|
@@ -6,7 +6,7 @@ module Valkyrie::Persistence::Solr
|
|
6
6
|
# Most methods are delegated to {Valkyrie::Persistence::Solr::Repository}
|
7
7
|
class Persister
|
8
8
|
attr_reader :adapter
|
9
|
-
delegate :connection, :resource_factory, to: :adapter
|
9
|
+
delegate :connection, :query_service, :resource_factory, :write_only?, :soft_commit?, to: :adapter
|
10
10
|
|
11
11
|
# @param adapter [Valkyrie::Persistence::Solr::MetadataAdapter] The adapter with the
|
12
12
|
# configured solr connection.
|
@@ -15,11 +15,23 @@ module Valkyrie::Persistence::Solr
|
|
15
15
|
end
|
16
16
|
|
17
17
|
# (see Valkyrie::Persistence::Memory::Persister#save)
|
18
|
-
|
19
|
-
|
18
|
+
# @return [Boolean] If write_only, whether saving succeeded.
|
19
|
+
def save(resource:, external_resource: false)
|
20
|
+
if write_only?
|
21
|
+
repository([resource]).persist
|
22
|
+
else
|
23
|
+
raise Valkyrie::Persistence::ObjectNotFoundError, "The object #{resource.id} is previously persisted but not found at save time." unless external_resource || valid_for_save?(resource)
|
24
|
+
repository([resource]).persist.first
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def valid_for_save?(resource)
|
29
|
+
return true unless resource.persisted? # a new resource
|
30
|
+
query_service.find_by(id: resource.id).present? # a persisted resource must be found
|
20
31
|
end
|
21
32
|
|
22
33
|
# (see Valkyrie::Persistence::Memory::Persister#save_all)
|
34
|
+
# @return [Boolean] If write_only, whether saving succeeded.
|
23
35
|
def save_all(resources:)
|
24
36
|
repository(resources).persist
|
25
37
|
end
|
@@ -39,7 +51,7 @@ module Valkyrie::Persistence::Solr
|
|
39
51
|
# @param [Array<Valkyrie::Resource>] resources
|
40
52
|
# @return [Valkyrie::Persistence::Solr::Repository]
|
41
53
|
def repository(resources)
|
42
|
-
Valkyrie::Persistence::Solr::Repository.new(resources: resources,
|
54
|
+
Valkyrie::Persistence::Solr::Repository.new(resources: resources, persister: self)
|
43
55
|
end
|
44
56
|
end
|
45
57
|
end
|
@@ -3,17 +3,18 @@ module Valkyrie::Persistence::Solr
|
|
3
3
|
# Responsible for handling the logic for persisting or deleting multiple
|
4
4
|
# objects into or out of solr.
|
5
5
|
class Repository
|
6
|
-
|
6
|
+
SOFT_COMMIT_PARAMS = { softCommit: true, versions: true }.freeze
|
7
|
+
NO_COMMIT_PARAMS = { versions: true }.freeze
|
7
8
|
|
8
|
-
attr_reader :resources, :
|
9
|
+
attr_reader :resources, :persister
|
10
|
+
delegate :connection, :resource_factory, :write_only?, :soft_commit?, to: :persister
|
9
11
|
|
10
12
|
# @param [Array<Valkyrie::Resource>] resources
|
11
13
|
# @param [RSolr::Client] connection
|
12
14
|
# @param [ResourceFactory] resource_factory
|
13
|
-
def initialize(resources:,
|
15
|
+
def initialize(resources:, persister:)
|
14
16
|
@resources = resources
|
15
|
-
@
|
16
|
-
@resource_factory = resource_factory
|
17
|
+
@persister = persister
|
17
18
|
end
|
18
19
|
|
19
20
|
# Persist the resources into Solr
|
@@ -24,6 +25,7 @@ module Valkyrie::Persistence::Solr
|
|
24
25
|
solr_document(resource)
|
25
26
|
end
|
26
27
|
results = add_documents(documents)
|
28
|
+
return true if write_only?
|
27
29
|
versions = results["adds"]&.each_slice(2)&.to_h
|
28
30
|
documents.map do |document|
|
29
31
|
document["_version_"] = versions.fetch(document[:id])
|
@@ -35,7 +37,7 @@ module Valkyrie::Persistence::Solr
|
|
35
37
|
# @return [RSolr::HashWithResponse]
|
36
38
|
# rubocop:disable Style/IfUnlessModifier
|
37
39
|
def add_documents(documents)
|
38
|
-
connection.add documents, params:
|
40
|
+
connection.add documents, params: commit_params
|
39
41
|
rescue RSolr::Error::Http => exception
|
40
42
|
# Error 409 conflict is returned when versions do not match
|
41
43
|
if exception.response&.fetch(:status) == 409
|
@@ -48,7 +50,7 @@ module Valkyrie::Persistence::Solr
|
|
48
50
|
# Deletes a Solr Document using the ID
|
49
51
|
# @return [Array<Valkyrie::Resource>] resources which have been deleted from Solr
|
50
52
|
def delete
|
51
|
-
connection.delete_by_id resources.map { |resource| resource.id.to_s }, params:
|
53
|
+
connection.delete_by_id resources.map { |resource| resource.id.to_s }, params: commit_params
|
52
54
|
resources
|
53
55
|
end
|
54
56
|
|
@@ -75,5 +77,13 @@ module Valkyrie::Persistence::Solr
|
|
75
77
|
raise Valkyrie::Persistence::StaleObjectError, "One or more resources have been updated by another process." if resources.count > 1
|
76
78
|
raise Valkyrie::Persistence::StaleObjectError, "The object #{resources.first.id} has been updated by another process."
|
77
79
|
end
|
80
|
+
|
81
|
+
def commit_params
|
82
|
+
if persister.soft_commit?
|
83
|
+
SOFT_COMMIT_PARAMS
|
84
|
+
else
|
85
|
+
NO_COMMIT_PARAMS
|
86
|
+
end
|
87
|
+
end
|
78
88
|
end
|
79
89
|
end
|
@@ -11,7 +11,7 @@ module Valkyrie
|
|
11
11
|
# attribute :nested_resource
|
12
12
|
# end
|
13
13
|
#
|
14
|
-
# @see https://github.com/samvera/hydra-head/tree/
|
14
|
+
# @see https://github.com/samvera/hydra-head/tree/main/hydra-access-controls
|
15
15
|
# @see lib/valkyrie/indexers/access_controls_indexer/rb
|
16
16
|
module AccessControls
|
17
17
|
def self.included(klass)
|
data/lib/valkyrie/resource.rb
CHANGED
@@ -15,7 +15,6 @@ module Valkyrie
|
|
15
15
|
# @see lib/valkyrie/specs/shared_specs/resource.rb
|
16
16
|
# rubocop:disable Metrics/ClassLength
|
17
17
|
class Resource < Dry::Struct
|
18
|
-
include Draper::Decoratable
|
19
18
|
# Allows a Valkyrie::Resource to be instantiated without providing every
|
20
19
|
# available key, and makes sure the defaults are set up if no value is
|
21
20
|
# given.
|
@@ -9,6 +9,7 @@ RSpec.shared_examples 'a Valkyrie::StorageAdapter::File' do
|
|
9
9
|
it { is_expected.to respond_to(:read) }
|
10
10
|
it { is_expected.to respond_to(:rewind) }
|
11
11
|
it { is_expected.to respond_to(:id) }
|
12
|
+
it { is_expected.to respond_to(:close) }
|
12
13
|
describe "#disk_path" do
|
13
14
|
it "returns an existing disk path" do
|
14
15
|
expect(File.exist?(file.disk_path)).to eq true
|
@@ -50,6 +50,24 @@ RSpec.shared_examples 'a Valkyrie::Persister' do |*flags|
|
|
50
50
|
expect(reloaded.nested_resource.first.title).to eq ["Nested"]
|
51
51
|
end
|
52
52
|
|
53
|
+
context "when a persisted resource is not in the database" do
|
54
|
+
it "throws an ObjectNotFoundError" do
|
55
|
+
expect(resource).not_to be_persisted
|
56
|
+
saved = persister.save(resource: resource)
|
57
|
+
|
58
|
+
expect(saved).to be_persisted
|
59
|
+
persister.delete(resource: saved)
|
60
|
+
|
61
|
+
expect { persister.save(resource: saved) }.to raise_error(Valkyrie::Persistence::ObjectNotFoundError)
|
62
|
+
end
|
63
|
+
it "is okay if it's from another persister" do
|
64
|
+
memory_adapter = Valkyrie::Persistence::Memory::MetadataAdapter.new
|
65
|
+
saved = memory_adapter.persister.save(resource: resource)
|
66
|
+
|
67
|
+
expect { persister.save(resource: saved, external_resource: true) }.not_to raise_error
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
53
71
|
it "can persist single values" do
|
54
72
|
resource.single_value = "A single value"
|
55
73
|
|
@@ -351,7 +369,7 @@ RSpec.shared_examples 'a Valkyrie::Persister' do |*flags|
|
|
351
369
|
# update the resource in the datastore to make its token stale
|
352
370
|
persister.save(resource: resource)
|
353
371
|
|
354
|
-
expect { persister.save(resource: resource) }.to raise_error(Valkyrie::Persistence::StaleObjectError
|
372
|
+
expect { persister.save(resource: resource) }.to raise_error(Valkyrie::Persistence::StaleObjectError)
|
355
373
|
end
|
356
374
|
end
|
357
375
|
|
@@ -425,7 +443,7 @@ RSpec.shared_examples 'a Valkyrie::Persister' do |*flags|
|
|
425
443
|
persister.save(resource: resource2)
|
426
444
|
|
427
445
|
expect { persister.save_all(resources: [resource1, resource2, resource3]) }
|
428
|
-
.to raise_error(Valkyrie::Persistence::StaleObjectError
|
446
|
+
.to raise_error(Valkyrie::Persistence::StaleObjectError)
|
429
447
|
end
|
430
448
|
end
|
431
449
|
end
|
@@ -464,6 +464,13 @@ RSpec.shared_examples 'a Valkyrie query provider' do
|
|
464
464
|
end
|
465
465
|
end
|
466
466
|
|
467
|
+
describe ".custom_queries" do
|
468
|
+
it "raises NoMethodError when the custom query does not exist" do
|
469
|
+
expect(query_service.custom_queries).not_to respond_to :very_fake_query
|
470
|
+
expect { query_service.custom_queries.very_fake_query }.to raise_error(NoMethodError)
|
471
|
+
end
|
472
|
+
end
|
473
|
+
|
467
474
|
describe ".register_query_handler" do
|
468
475
|
it "can register a query handler" do
|
469
476
|
class QueryHandler
|
@@ -30,6 +30,25 @@ RSpec.shared_examples 'a Valkyrie::StorageAdapter' do
|
|
30
30
|
expect(uploaded_file.valid?(digests: { sha1: sha1 })).to be true
|
31
31
|
end
|
32
32
|
|
33
|
+
it "doesn't leave a file handle open on upload/find_by" do
|
34
|
+
# No file handle left open from upload.
|
35
|
+
resource = Valkyrie::Specs::CustomResource.new(id: "testdiscovery")
|
36
|
+
pre_open_files = open_files
|
37
|
+
uploaded_file = storage_adapter.upload(file: file, original_filename: 'foo.jpg', resource: resource, fake_upload_argument: true)
|
38
|
+
file.close
|
39
|
+
expect(pre_open_files.size).to eq open_files.size
|
40
|
+
|
41
|
+
# No file handle left open from find_by
|
42
|
+
pre_open_files = open_files
|
43
|
+
the_file = storage_adapter.find_by(id: uploaded_file.id)
|
44
|
+
expect(the_file).to be_kind_of Valkyrie::StorageAdapter::File
|
45
|
+
expect(pre_open_files.size).to eq open_files.size
|
46
|
+
end
|
47
|
+
|
48
|
+
def open_files
|
49
|
+
`lsof +D . | awk '{print $9}'`.split.uniq[1..-1]
|
50
|
+
end
|
51
|
+
|
33
52
|
it "can upload, validate, re-fetch, and delete a file" do
|
34
53
|
resource = Valkyrie::Specs::CustomResource.new(id: "test")
|
35
54
|
sha1 = Digest::SHA1.file(file).to_s
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
RSpec.shared_examples 'a write-only Valkyrie::MetadataAdapter' do |passed_adapter|
|
3
|
+
before do
|
4
|
+
raise 'adapter must be set with `let(:adapter)`' unless
|
5
|
+
defined? adapter
|
6
|
+
end
|
7
|
+
subject { passed_adapter || adapter }
|
8
|
+
let(:persister) { adapter.persister }
|
9
|
+
it { is_expected.to respond_to(:persister).with(0).arguments }
|
10
|
+
it { is_expected.to respond_to(:id).with(0).arguments }
|
11
|
+
|
12
|
+
describe "#id" do
|
13
|
+
it "is a valid string representation of an MD5 hash" do
|
14
|
+
expect(adapter.id).to be_a Valkyrie::ID
|
15
|
+
expect(adapter.id.to_s.length).to eq 32
|
16
|
+
expect(adapter.id.to_s).to match(/^[a-f,0-9]+$/)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "#write_only?" do
|
21
|
+
it "returns true" do
|
22
|
+
expect(adapter).to be_write_only
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "persister" do
|
27
|
+
before do
|
28
|
+
class WriteOnlyCustomResource < Valkyrie::Resource
|
29
|
+
include Valkyrie::Resource::AccessControls
|
30
|
+
attribute :title
|
31
|
+
attribute :author
|
32
|
+
attribute :other_author
|
33
|
+
attribute :member_ids
|
34
|
+
attribute :nested_resource
|
35
|
+
attribute :single_value, Valkyrie::Types::String.optional
|
36
|
+
attribute :ordered_authors, Valkyrie::Types::Array.of(Valkyrie::Types::Anything).meta(ordered: true)
|
37
|
+
attribute :ordered_nested, Valkyrie::Types::Array.of(WriteOnlyCustomResource).meta(ordered: true)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
after do
|
41
|
+
Object.send(:remove_const, :WriteOnlyCustomResource)
|
42
|
+
end
|
43
|
+
|
44
|
+
subject { persister }
|
45
|
+
let(:resource_class) { WriteOnlyCustomResource }
|
46
|
+
let(:resource) { resource_class.new }
|
47
|
+
|
48
|
+
it { is_expected.to respond_to(:save).with_keywords(:resource) }
|
49
|
+
it { is_expected.to respond_to(:save_all).with_keywords(:resources) }
|
50
|
+
it { is_expected.to respond_to(:delete).with_keywords(:resource) }
|
51
|
+
|
52
|
+
it "can save a resource" do
|
53
|
+
expect(persister.save(resource: resource)).to eq true
|
54
|
+
end
|
55
|
+
|
56
|
+
it "can save multiple resources at once" do
|
57
|
+
resource2 = resource_class.new
|
58
|
+
results = persister.save_all(resources: [resource, resource2])
|
59
|
+
expect(results).to eq true
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -13,3 +13,5 @@ require 'valkyrie/specs/shared_specs/change_set_persister.rb'
|
|
13
13
|
require 'valkyrie/specs/shared_specs/file.rb'
|
14
14
|
require 'valkyrie/specs/shared_specs/change_set.rb'
|
15
15
|
require 'valkyrie/specs/shared_specs/solr_indexer.rb'
|
16
|
+
# Write-only tests.
|
17
|
+
require 'valkyrie/specs/shared_specs/write_only/metadata_adapter.rb'
|
@@ -36,11 +36,34 @@ module Valkyrie::Storage
|
|
36
36
|
# @return [Valkyrie::StorageAdapter::File]
|
37
37
|
# @raise Valkyrie::StorageAdapter::FileNotFound if nothing is found
|
38
38
|
def find_by(id:)
|
39
|
-
Valkyrie::StorageAdapter::File.new(id: Valkyrie::ID.new(id.to_s), io:
|
39
|
+
Valkyrie::StorageAdapter::File.new(id: Valkyrie::ID.new(id.to_s), io: LazyFile.open(file_path(id), 'rb'))
|
40
40
|
rescue Errno::ENOENT
|
41
41
|
raise Valkyrie::StorageAdapter::FileNotFound
|
42
42
|
end
|
43
43
|
|
44
|
+
## LazyFile takes File.open parameters but doesn't leave a file handle open on
|
45
|
+
# instantiation. This way StorageAdapter#find_by doesn't open a handle
|
46
|
+
# silently and never clean up after itself.
|
47
|
+
class LazyFile
|
48
|
+
def self.open(path, mode)
|
49
|
+
# Open the file regularly and close it, so it can error if it doesn't
|
50
|
+
# exist.
|
51
|
+
File.open(path, mode).close
|
52
|
+
new(path, mode)
|
53
|
+
end
|
54
|
+
|
55
|
+
delegate(*(File.instance_methods - Object.instance_methods), to: :_inner_file)
|
56
|
+
|
57
|
+
def initialize(path, mode)
|
58
|
+
@__path = path
|
59
|
+
@__mode = mode
|
60
|
+
end
|
61
|
+
|
62
|
+
def _inner_file
|
63
|
+
@_inner_file ||= File.open(@__path, @__mode)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
44
67
|
# Delete the file on disk associated with the given identifier.
|
45
68
|
# @param id [Valkyrie::ID]
|
46
69
|
def delete(id:)
|
data/lib/valkyrie/version.rb
CHANGED
data/lib/valkyrie.rb
CHANGED
@@ -5,7 +5,6 @@ require 'active_support'
|
|
5
5
|
require 'active_support/core_ext'
|
6
6
|
require 'dry-types'
|
7
7
|
require 'dry-struct'
|
8
|
-
require 'draper'
|
9
8
|
require 'reform'
|
10
9
|
require 'rdf'
|
11
10
|
require 'valkyrie/rdf_patches'
|
@@ -87,19 +86,6 @@ module Valkyrie
|
|
87
86
|
Valkyrie::StorageAdapter.find(super.to_sym)
|
88
87
|
end
|
89
88
|
|
90
|
-
# @api public
|
91
|
-
# Configure id_string_equality to be true in order to make Valkyrie::ID
|
92
|
-
# equal to the string value they contain. This will be the default behavior
|
93
|
-
# in v3.0.0.
|
94
|
-
#
|
95
|
-
# @return [Boolean] Whether `Valkyrie::ID` should be equal to their string counterpart.
|
96
|
-
def id_string_equality
|
97
|
-
super
|
98
|
-
end
|
99
|
-
|
100
|
-
# @!attribute [w] id_string_equality=
|
101
|
-
# The setter for #id_string_equality; see it's implementation
|
102
|
-
|
103
89
|
# @api public
|
104
90
|
#
|
105
91
|
# The returned anonymous method (e.g. responds to #call) has a signature of
|
data/valkyrie.gemspec
CHANGED
@@ -21,7 +21,6 @@ Gem::Specification.new do |spec|
|
|
21
21
|
spec.require_paths = ["lib"]
|
22
22
|
|
23
23
|
spec.add_dependency 'dry-struct'
|
24
|
-
spec.add_dependency 'draper'
|
25
24
|
spec.add_dependency 'activemodel'
|
26
25
|
spec.add_dependency 'dry-types', '~> 1.0'
|
27
26
|
spec.add_dependency 'rdf', '~> 3.0', '>= 3.0.10'
|
@@ -32,7 +31,6 @@ Gem::Specification.new do |spec|
|
|
32
31
|
spec.add_dependency 'json-ld'
|
33
32
|
spec.add_dependency 'json'
|
34
33
|
spec.add_dependency 'rdf-vocab'
|
35
|
-
spec.add_dependency 'disposable', '~> 0.4.5'
|
36
34
|
spec.add_dependency 'faraday', '< 1.0'
|
37
35
|
|
38
36
|
spec.add_development_dependency "bundler", "> 1.16.0", "< 3"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: valkyrie
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 3.0.0.pre.beta.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Trey Pendragon
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-10-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: dry-struct
|
@@ -24,20 +24,6 @@ dependencies:
|
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: draper
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - ">="
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: '0'
|
34
|
-
type: :runtime
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - ">="
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: '0'
|
41
27
|
- !ruby/object:Gem::Dependency
|
42
28
|
name: activemodel
|
43
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -184,20 +170,6 @@ dependencies:
|
|
184
170
|
- - ">="
|
185
171
|
- !ruby/object:Gem::Version
|
186
172
|
version: '0'
|
187
|
-
- !ruby/object:Gem::Dependency
|
188
|
-
name: disposable
|
189
|
-
requirement: !ruby/object:Gem::Requirement
|
190
|
-
requirements:
|
191
|
-
- - "~>"
|
192
|
-
- !ruby/object:Gem::Version
|
193
|
-
version: 0.4.5
|
194
|
-
type: :runtime
|
195
|
-
prerelease: false
|
196
|
-
version_requirements: !ruby/object:Gem::Requirement
|
197
|
-
requirements:
|
198
|
-
- - "~>"
|
199
|
-
- !ruby/object:Gem::Version
|
200
|
-
version: 0.4.5
|
201
173
|
- !ruby/object:Gem::Dependency
|
202
174
|
name: faraday
|
203
175
|
requirement: !ruby/object:Gem::Requirement
|
@@ -533,6 +505,7 @@ files:
|
|
533
505
|
- lib/valkyrie/specs/shared_specs/resource.rb
|
534
506
|
- lib/valkyrie/specs/shared_specs/solr_indexer.rb
|
535
507
|
- lib/valkyrie/specs/shared_specs/storage_adapter.rb
|
508
|
+
- lib/valkyrie/specs/shared_specs/write_only/metadata_adapter.rb
|
536
509
|
- lib/valkyrie/storage.rb
|
537
510
|
- lib/valkyrie/storage/disk.rb
|
538
511
|
- lib/valkyrie/storage/fedora.rb
|
@@ -576,9 +549,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
576
549
|
version: '0'
|
577
550
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
578
551
|
requirements:
|
579
|
-
- - "
|
552
|
+
- - ">"
|
580
553
|
- !ruby/object:Gem::Version
|
581
|
-
version:
|
554
|
+
version: 1.3.1
|
582
555
|
requirements: []
|
583
556
|
rubygems_version: 3.0.3
|
584
557
|
signing_key:
|