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.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +12 -2
  3. data/.lando.yml +1 -1
  4. data/.rubocop.yml +7 -0
  5. data/CHANGELOG.md +39 -13
  6. data/CONTRIBUTING.md +30 -8
  7. data/README.md +5 -1
  8. data/lib/valkyrie/id.rb +1 -16
  9. data/lib/valkyrie/persistence/buffered_persister.rb +2 -2
  10. data/lib/valkyrie/persistence/composite_persister.rb +3 -3
  11. data/lib/valkyrie/persistence/custom_query_container.rb +8 -16
  12. data/lib/valkyrie/persistence/fedora/persister/model_converter.rb +0 -1
  13. data/lib/valkyrie/persistence/fedora/persister/orm_converter.rb +1 -26
  14. data/lib/valkyrie/persistence/fedora/persister.rb +3 -1
  15. data/lib/valkyrie/persistence/memory/persister.rb +18 -2
  16. data/lib/valkyrie/persistence/postgres/persister.rb +4 -1
  17. data/lib/valkyrie/persistence/solr/metadata_adapter.rb +15 -3
  18. data/lib/valkyrie/persistence/solr/model_converter.rb +8 -25
  19. data/lib/valkyrie/persistence/solr/orm_converter.rb +1 -1
  20. data/lib/valkyrie/persistence/solr/persister.rb +16 -4
  21. data/lib/valkyrie/persistence/solr/repository.rb +17 -7
  22. data/lib/valkyrie/resource/access_controls.rb +1 -1
  23. data/lib/valkyrie/resource.rb +0 -1
  24. data/lib/valkyrie/specs/shared_specs/file.rb +1 -0
  25. data/lib/valkyrie/specs/shared_specs/persister.rb +20 -2
  26. data/lib/valkyrie/specs/shared_specs/queries.rb +7 -0
  27. data/lib/valkyrie/specs/shared_specs/storage_adapter.rb +19 -0
  28. data/lib/valkyrie/specs/shared_specs/write_only/metadata_adapter.rb +62 -0
  29. data/lib/valkyrie/specs/shared_specs.rb +2 -0
  30. data/lib/valkyrie/storage/disk.rb +24 -1
  31. data/lib/valkyrie/storage_adapter.rb +1 -1
  32. data/lib/valkyrie/version.rb +1 -1
  33. data/lib/valkyrie.rb +0 -14
  34. data/valkyrie.gemspec +0 -2
  35. metadata +5 -32
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a9562026fbee014b2e1f3076826af96c850373e9ff94eeb1921facbad11edcec
4
- data.tar.gz: '0119066f0cf241db23c9a29d7f9501803c669df197bda350f0706d2b2db6120d'
3
+ metadata.gz: 240eb74a4be15202ae2036ac9a0067b1aadc23e65ad583638b6cd2874540a845
4
+ data.tar.gz: 65568d10c48dcadbee70bff7c23360a6e0b149f874b5ebe493b5770afb6e4362
5
5
  SHA512:
6
- metadata.gz: 63279c6ea99e08f10dcb8b6aa6cd6fada5d60937852dde46ad08b2923e3d129eb03442be0d4a11640affefeda7367fdabc9d93bb6c90f53b25c71fc12dea7cc4
7
- data.tar.gz: cd471fa3caa33b644cca4c5897104225dbdc3fafe0d10a889d102a03e6e5ba74aa9284cd5fc27933ded7c842e2d09f0326d66119290df557f7ca1c29e2c7f2e2
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-beta-1
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-get update && sudo apt-get install -y libpq-dev
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
@@ -44,7 +44,7 @@ services:
44
44
  volumes:
45
45
  fedora6:
46
46
  services:
47
- image: fcrepo/fcrepo:6.0.0-beta-1
47
+ image: fcrepo/fcrepo:6.0.0
48
48
  command:
49
49
  - "catalina.sh"
50
50
  - "run"
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
- [hackmastera](https://github.com/hackmastera)
89
+ [hackartisan](https://github.com/hackartisan)
64
90
  * Fix Rubocop for latest Bixby.
65
- [hackmastera](https://github.com/hackmastera)
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
- [hackmastera](https://github.com/hackmastera)
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
- [hackmastera](https://github.com/hackmastera)
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
- [hackmastera](https://github.com/hackmastera)
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
- [hackmastera](https://github.com/hackmastera)
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
- [hackmastera](https://github.com/hackmastera)
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
- [hackmastera](https://github.com/hackmastera)
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
- [hackmastera](https://github.com/hackmastera)
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
- [hackmastera](https://github.com/hackmastera)
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
- * [hackmastera](https://github.com/hackmastera)
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
- [hackmastera](https://github.com/hackmastera)
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
- * [hackmastera](https://github.com/hackmastera)
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 master branch.
49
- * To quickly create a topic branch based on master; `git branch fix/master/my_contribution master`
50
- * Then checkout the new branch with `git checkout fix/master/my_contribution`.
51
- * Please avoid working directly on the `master` branch.
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. master)
113
- * `git checkout master`
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 master`
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 master` ([See Github help](https://help.github.com/articles/interactive-rebase))
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
- return string_equality(other) if Valkyrie.config.id_string_equality == true
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
- @query_handlers << query_handler
47
- end
48
-
49
- def method_missing(meth_name, *args, &block)
50
- query_handler = find_query_handler(meth_name).new(query_service: query_service)
51
- return super unless query_handler
52
- query_handler.__send__(meth_name, *args, &block)
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
- def initialize(connection:, resource_indexer: NullIndexer)
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/master/solr/config/schema.xml
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 = Array.wrap(fields)
166
- @values = Array.wrap(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/master/solr/config/schema.xml
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/master/solr/config/schema.xml
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/master/solr/config/schema.xml
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
- def save(resource:)
19
- repository([resource]).persist.first
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, connection: connection, resource_factory: resource_factory)
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
- COMMIT_PARAMS = { softCommit: true, versions: true }.freeze
6
+ SOFT_COMMIT_PARAMS = { softCommit: true, versions: true }.freeze
7
+ NO_COMMIT_PARAMS = { versions: true }.freeze
7
8
 
8
- attr_reader :resources, :connection, :resource_factory
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:, connection:, resource_factory:)
15
+ def initialize(resources:, persister:)
14
16
  @resources = resources
15
- @connection = connection
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: COMMIT_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: COMMIT_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/master/hydra-access-controls
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)
@@ -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, "The object #{resource.id} has been updated by another process.")
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, "One or more resources have been updated by another process.")
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: ::File.open(file_path(id), 'rb'))
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:)
@@ -67,7 +67,7 @@ module Valkyrie
67
67
  class File < Dry::Struct
68
68
  attribute :id, Valkyrie::Types::Any
69
69
  attribute :io, Valkyrie::Types::Any
70
- delegate :size, :read, :rewind, to: :io
70
+ delegate :size, :read, :rewind, :close, to: :io
71
71
  def stream
72
72
  io
73
73
  end
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module Valkyrie
3
- VERSION = "2.2.0"
3
+ VERSION = "3.0.0-beta.1"
4
4
  end
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: 2.2.0
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-05-06 00:00:00.000000000 Z
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: '0'
554
+ version: 1.3.1
582
555
  requirements: []
583
556
  rubygems_version: 3.0.3
584
557
  signing_key: