valkyrie 2.2.0 → 3.0.0.pre.beta.1

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