valkyrie 2.2.0 → 3.0.0.pre.beta.1
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.
- 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:
|