valkyrie 1.4.0 → 1.5.0.RC1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA256:
3
- metadata.gz: d66ba588c11dfaa509ab8dfe2c43a1018462103121e43fecb07e06e31878155d
4
- data.tar.gz: 235551dbdb14be9f7a2034cbcf6a288ebbe6a11511cac31ea1a62fb3dcadfa39
2
+ SHA1:
3
+ metadata.gz: f51055953266a92fd0ffcdd70c1204545668897e
4
+ data.tar.gz: 32effaf156970e235ef047d3198874b3a538533c
5
5
  SHA512:
6
- metadata.gz: d9f8eafb124d7db30b9b6ab51165327877ac9dbd25a71cc8e074eec99880fa64bce7864abaad28ea81d65634567b8937332d42b27046b42490890158ee10da1c
7
- data.tar.gz: 9b2877caed49d8e4e1aff11050391c2ccb8bc8259428e69786f3cc0256fc536f3afb05da40ca98e9878024ccacc4367d216cc61f60c18d2f446f1157e479daaa
6
+ metadata.gz: d411d99edb4ed3693b03cf40aeee8bcde69f510eaddfd0c28c2ca271480b541325a72d370c1b4f9c12422d8858a3d0e2e49abdb70623d664a21c8ca10cbcd826
7
+ data.tar.gz: 452bead204181f78df71e2f67f33b0d87f67510bc6c23e7a5eada9e7561f15de1a409370abcef24605e16d564066f902dafea50dc467ad9054729eb9edb2ab85
data/.circleci/config.yml CHANGED
@@ -11,6 +11,9 @@ jobs:
11
11
  curl -L https://github.com/docker/compose/releases/download/1.19.0/docker-compose-`uname -s`-`uname -m` > ~/docker-compose
12
12
  chmod +x ~/docker-compose
13
13
  sudo mv ~/docker-compose /usr/local/bin/docker-compose
14
+ - run:
15
+ name: Update bundler
16
+ command: gem install bundler -v 1.16.2
14
17
  - restore_cache:
15
18
  keys:
16
19
  - bundle-{{ checksum "Gemfile" }}-{{ checksum "valkyrie.gemspec" }}
data/CHANGELOG.md CHANGED
@@ -1,3 +1,47 @@
1
+ # v1.5.0 RC1 2018-02-01
2
+
3
+ ## Changes since last release
4
+
5
+ * Add missing query service requirement to persister shared specs
6
+ [cjcolvar](https://github.com/cjcolvar)
7
+ * Provide a warning when postgres adapter overwrites an ID, deprecate this
8
+ behavior so it will throw an exception in the future.
9
+ [cam156](https://github.com/cam156)
10
+ [hackmastera](https://github.com/hackmastera)
11
+ [tpendragon](https://github.com/tpendragon)
12
+ * Add support for passing just an ID to find_inverse_references_by
13
+ [cam156](https://github.com/cam156)
14
+ [hackmastera](https://github.com/hackmastera)
15
+ * Fix memory adapter raising an exception in find_by_alternate_identifier when
16
+ there are resources without the alternate_identifier attribute.
17
+ [jeremyf](https://github.com/jeremyf)
18
+ * Provide a warning when using the postgres adapter without manually providing
19
+ the pg gem, so it can be an optional dependency in 2.0.0.
20
+ [hackmastera](https://github.com/hackmastera)
21
+ * Provide guidance in specs on how to define alternate_ids
22
+ [cjcolvar](https://github.com/cjcolvar)
23
+ * Upload files to Fedora using form/multipart.
24
+ [tpendragon](https://github.com/tpendragon)
25
+ * Improve CompositePersister documentation.
26
+ [tpendragon](https://github.com/tpendragon)
27
+ * Add a Valkyrie::Types::Params::ID type which handles when an HTML form passes
28
+ an empty string value.
29
+ [tpendragon](https://github.com/tpendragon)
30
+ * Deprecate .member on Valkyrie::Types::Array & Set
31
+ [tpendragon](https://github.com/tpendragon)
32
+ * Fix updated_at not being set correctly for the Solr adapter, fix shared specs.
33
+ [tpendragon](https://github.com/tpendragon)
34
+
35
+ Additional thanks to the following for code review and issue reports leading to
36
+ this release:
37
+
38
+ [awead](https://github.com/awead)
39
+ [escowles](https://github.com/escowles)
40
+ [kelynch](https://github.com/kelynch)
41
+ [mbklein](https://github.com/mbklein)
42
+ [no-reply](https://github.com/no-reply)
43
+ [revgum](https://github.com/revgum)
44
+
1
45
  # v1.4.0 2018-01-08
2
46
 
3
47
  ## Changes since last release.
@@ -0,0 +1,36 @@
1
+ The Samvera community is dedicated to providing a welcoming and
2
+ positive experience for all its members, whether they are at a formal
3
+ gathering, in a social setting, or taking part in activities online.
4
+ The Samvera community welcomes participation from people all over the
5
+ world and these members bring with them a wide variety of
6
+ professional, personal and social backgrounds; whatever these may be,
7
+ we treat colleagues with dignity and respect.
8
+
9
+ Community members communicate primarily in English, though for many of
10
+ them this is not their native language. We therefore strive to express
11
+ ourselves simply and clearly remembering that unnecessary use of
12
+ jargon and slang will be a barrier to understanding for many of our
13
+ colleagues. We are sensitive to the fact that the international
14
+ nature of the community means that we span many different social norms
15
+ around language and behaviour and we strive to conduct ourselves,
16
+ online and in person, in ways that are unlikely to cause offence.
17
+
18
+ Samvera conversations are often information-rich and intended to
19
+ generate discussion and debate. We discuss ideas from a standpoint of
20
+ mutual respect and reasoned argument.
21
+
22
+ Community members work together to promote a respectful and safe
23
+ community. In the event that someone’s conduct is causing offence or
24
+ distress, Samvera has a detailed
25
+ [Anti-Harassment Policy and Protocol](https://wiki.duraspace.org/display/samvera/Anti-Harassment+Policy)
26
+ which can be applied to address the problem. The first step in dealing
27
+ with any serious misconduct is to contact a local meeting organizer,
28
+ the
29
+ [Samvera community helpers](https://wiki.duraspace.org/display/samvera/Samvera+Community+Helpers)
30
+ ([email](mailto:helpers@samvera.org)), a community member you
31
+ trust, or the
32
+ [Samvera Steering Group](https://wiki.duraspace.org/display/samvera/Samvera+Steering+Group+membership)
33
+ immediately; at Samvera events, these people can be identified by
34
+ distinctive name badges. The
35
+ [Policy and Protocol](https://wiki.duraspace.org/display/samvera/Anti-Harassment+Policy)
36
+ should be consulted for fuller details.
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,161 @@
1
+ # How to Contribute
2
+
3
+ We want your help to make the Samvera community great. There are a few guidelines
4
+ that we need contributors to follow so that we can have a chance of
5
+ keeping on top of things.
6
+
7
+ ## Code of Conduct
8
+
9
+ The Samvera Community is dedicated to providing a welcoming and positive
10
+ experience for all its members, whether they are at a formal gathering, in
11
+ a social setting, or taking part in activities online. Please see our
12
+ [Code of Conduct](CODE_OF_CONDUCT.md) for more information.
13
+
14
+ ## Samvera Community Intellectual Property Licensing and Ownership
15
+
16
+ All code contributors must have an Individual Contributor License Agreement
17
+ (iCLA) on file with the Samvera Steering Group. If the contributor works for
18
+ an institution, the institution must have a Corporate Contributor License
19
+ Agreement (cCLA) on file.
20
+
21
+ https://wiki.duraspace.org/display/samvera/Samvera+Community+Intellectual+Property+Licensing+and+Ownership
22
+
23
+ You should also add yourself to the `CONTRIBUTORS.md` file in the root of the project.
24
+
25
+ ## Contribution Tasks
26
+
27
+ * Reporting Issues
28
+ * Making Changes
29
+ * Documenting Code
30
+ * Committing Changes
31
+ * Submitting Changes
32
+ * Reviewing and Merging Changes
33
+
34
+ ### Reporting Issues
35
+
36
+ * Make sure you have a [GitHub account](https://github.com/signup/free)
37
+ * Submit a [Github issue](https://github.com/samvera-labs/valkyrie/issues/) by:
38
+ * Clearly describing the issue
39
+ * Provide a descriptive summary
40
+ * Explain the expected behavior
41
+ * Explain the actual behavior
42
+ * Provide steps to reproduce the actual behavior
43
+
44
+ ### Making Changes
45
+
46
+ * Fork the repository on GitHub
47
+ * 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.
52
+ * You may find the [hub suite of commands](https://github.com/defunkt/hub) helpful
53
+ * Make sure you have added sufficient tests and documentation for your changes.
54
+ * Test functionality with RSpec; Test features / UI with Capybara.
55
+ * Run _all_ the tests to assure nothing else was accidentally broken.
56
+
57
+ ### Documenting Code
58
+
59
+ * All new public methods, modules, and classes should include inline documentation in [YARD](http://yardoc.org/).
60
+ * Documentation should seek to answer the question "why does this code exist?"
61
+ * Document private / protected methods as desired.
62
+ * If you are working in a file with no prior documentation, do try to document as you gain understanding of the code.
63
+ * If you don't know exactly what a bit of code does, it is extra likely that it needs to be documented. Take a stab at it and ask for feedback in your pull request. You can use the 'blame' button on GitHub to identify the original developer of the code and @mention them in your comment.
64
+ * This work greatly increases the usability of the code base and supports the on-ramping of new committers.
65
+ * We will all be understanding of one another's time constraints in this area.
66
+ * [Getting started with YARD](http://www.rubydoc.info/gems/yard/file/docs/GettingStarted.md)
67
+
68
+ ### Committing Changes
69
+
70
+ * Make commits of logical units.
71
+ * Check for unnecessary whitespace with `git diff --check` before committing.
72
+ * Make sure your commit messages are [well formed](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html).
73
+ * If you created an issue, you can close it by including "Closes #issue" in your commit message. See [Github's blog post for more details](https://github.com/blog/1386-closing-issues-via-commit-messages)
74
+
75
+ ```
76
+ Present tense short summary (50 characters or less)
77
+
78
+ More detailed description, if necessary. It should be wrapped to 72
79
+ characters. Try to be as descriptive as you can, even if you think that
80
+ the commit content is obvious, it may not be obvious to others. You
81
+ should add such description also if it's already present in bug tracker,
82
+ it should not be necessary to visit a webpage to check the history.
83
+
84
+ Include Closes #<issue-number> when relavent.
85
+
86
+ Description can have multiple paragraphs and you can use code examples
87
+ inside, just indent it with 4 spaces:
88
+
89
+ class PostsController
90
+ def index
91
+ respond_to do |wants|
92
+ wants.html { render 'index' }
93
+ end
94
+ end
95
+ end
96
+
97
+ You can also add bullet points:
98
+
99
+ - you can use dashes or asterisks
100
+
101
+ - also, try to indent next line of a point for readability, if it's too
102
+ long to fit in 72 characters
103
+ ```
104
+
105
+ * Make sure you have added the necessary tests for your changes.
106
+ * Run _all_ the tests to assure nothing else was accidentally broken.
107
+ * Then you are ready to submit a pull request
108
+
109
+ ### Submitting Changes
110
+
111
+ * 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`
114
+ * `git pull --rebase`
115
+ * `git checkout <your-branch>`
116
+ * `git rebase master`
117
+ * It is a good idea to run your tests again.
118
+ * If you've made more than one commit take a moment to consider whether squashing commits together would help improve their logical grouping.
119
+ * [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))
121
+ * 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
+ * Push your changes to a topic branch in your fork of the repository.
123
+ * Submit a pull request from your fork to the project.
124
+
125
+ ### Reviewing and Merging Changes
126
+
127
+ We adopted [Github's Pull Request Review](https://help.github.com/articles/about-pull-request-reviews/) for our repositories.
128
+ Common checks that may occur in our repositories:
129
+
130
+ 1. Travis CI - where our automated tests are running
131
+ 2. Approval Required - Github enforces at least one person approve a pull request. Also, all reviewers that have chimed in must approve.
132
+
133
+ If one or more of the required checks failed (or are incomplete), the code should not be merged (and the UI will not allow it). If all of the checks have passed, then anyone on the project (including the pull request submitter) may merge the code.
134
+
135
+ *Example: Carolyn submits a pull request, Justin reviews the pull request and approves. However, Justin is still waiting on other checks (Travis CI is usually the culprit), so he does not merge the pull request. Eventually, all of the checks pass. At this point, Carolyn or anyone else may merge the pull request.*
136
+
137
+ #### Things to Consider When Reviewing
138
+
139
+ First, the person contributing the code is putting themselves out there. Be mindful of what you say in a review.
140
+
141
+ * Ask clarifying questions
142
+ * State your understanding and expectations
143
+ * Provide example code or alternate solutions, and explain why
144
+
145
+ This is your chance for a mentoring moment of another developer. Take time to give an honest and thorough review of what has changed. Things to consider:
146
+
147
+ * Does the commit message explain what is going on?
148
+ * Does the code changes have tests? _Not all changes need new tests, some changes are refactorings_
149
+ * Do new or changed methods, modules, and classes have documentation?
150
+ * Does the commit contain more than it should? Are two separate concerns being addressed in one commit?
151
+ * Does the description of the new/changed specs match your understanding of what the spec is doing?
152
+ * Did the Travis tests complete successfully?
153
+
154
+ If you are uncertain, bring other contributors into the conversation by assigning them as a reviewer.
155
+
156
+ # Additional Resources
157
+
158
+ * [General GitHub documentation](http://help.github.com/)
159
+ * [GitHub pull request documentation](https://help.github.com/articles/about-pull-requests/)
160
+ * [Pro Git](http://git-scm.com/book) is both a free and excellent book about Git.
161
+ * [A Git Config for Contributing](http://ndlib.github.io/practices/my-typical-per-project-git-config/)
data/README.md CHANGED
@@ -4,11 +4,17 @@ Valkyrie is a gem for enabling multiple backends for storage of files and metada
4
4
 
5
5
  ![Valkyrie Logo](valkyrie_logo.png)
6
6
 
7
- [![CircleCI](https://circleci.com/gh/samvera-labs/valkyrie.svg?style=svg)](https://circleci.com/gh/samvera-labs/valkyrie)
7
+ Code: [![Version](https://badge.fury.io/rb/valkyrie.png)](http://badge.fury.io/rb/valkyrie)
8
+ [![Build Status](https://circleci.com/gh/samvera-labs/valkyrie.svg?style=svg)](https://circleci.com/gh/samvera-labs/valkyrie)
8
9
  [![Coverage Status](https://coveralls.io/repos/github/samvera-labs/valkyrie/badge.svg?branch=master)](https://coveralls.io/github/samvera-labs/valkyrie?branch=master)
9
10
  [![Stories in Ready](https://badge.waffle.io/samvera-labs/valkyrie.png?label=ready&title=Ready)](https://waffle.io/samvera-labs/valkyrie)
11
+
12
+ Docs: [![Contribution Guidelines](http://img.shields.io/badge/CONTRIBUTING-Guidelines-blue.svg)](./CONTRIBUTING.md)
13
+ [![Apache 2.0 License](http://img.shields.io/badge/APACHE2-license-blue.svg)](./LICENSE)
10
14
  [![Yard Docs](http://img.shields.io/badge/yard-docs-blue.svg)](http://rubydoc.info/github/samvera-labs/valkyrie)
11
15
 
16
+ Jump in: [![Slack Status](http://slack.samvera.org/badge.svg)](http://slack.samvera.org/)
17
+
12
18
  ## Primary Contacts
13
19
 
14
20
  ### Product Owner
@@ -19,7 +25,11 @@ Valkyrie is a gem for enabling multiple backends for storage of files and metada
19
25
 
20
26
  [Trey Pendragon](https://github.com/tpendragon)
21
27
 
22
- ## Installation
28
+ ## Help
29
+
30
+ The Samvera community is here to help. Please see our [support guide](./SUPPORT.md).
31
+
32
+ ## Getting Started
23
33
 
24
34
  Add this line to your application's Gemfile:
25
35
 
@@ -89,6 +99,17 @@ The initializer also registers three `Valkyrie::StorageAdapter` instances for st
89
99
  * `:memory` which stores files in an in-memory cache (again, not persistent, so this is only appropriate for
90
100
  testing)
91
101
 
102
+ ### Sample configuration with custom `Valkyrie.config.resource_class_resolver`:
103
+
104
+ ```
105
+ require 'valkyrie'
106
+ Rails.application.config.to_prepare do
107
+ Valkyrie.config.resource_class_resolver = lambda do |resource_klass_name|
108
+ # Do complicated lookup based on the string
109
+ end
110
+ end
111
+ ```
112
+
92
113
  ### Sample configuration: `config/valkyrie.yml`:
93
114
 
94
115
  A sample configuration file that configures your application to use different adapters:
@@ -249,17 +270,12 @@ When configuring your adapter, include the `fedora_version` parameter in your me
249
270
 
250
271
  The development and test stacks use fully contained virtual volumes and bind all services to different ports, so they can be running at the same time without issue.
251
272
 
252
- ## Get Help
253
-
254
- If you have any questions regarding Valkyrie you can send a message to [the
255
- Samvera community tech list](mailto:samvera-tech@googlegroups.com) or the `#valkyrie`
256
- channel in the [Samvera community Slack
257
- team](https://wiki.duraspace.org/pages/viewpage.action?pageId=87460391#Getintouch!-Slack).
258
-
259
- ## License
273
+ ## Acknowledgments
260
274
 
261
- Valkyrie is available under [the Apache 2.0 license](../LICENSE).
275
+ This software has been developed by and is brought to you by the Samvera community. Learn more at the
276
+ [Samvera website](http://samvera.org/).
262
277
 
278
+ ![Samvera Logo](https://wiki.duraspace.org/download/thumbnails/87459292/samvera-fall-font2-200w.png?version=1&modificationDate=1498550535816&api=v2)
263
279
 
264
280
  ## Contributing
265
281
 
data/SUPPORT.md ADDED
@@ -0,0 +1,5 @@
1
+ If you would like to report an issue, first search [the list of issues](https://github.com/samvera-labs/valkyrie/issues/) to see if someone else has already reported it, and then feel free to [create a new issue](https://github.com/samvera-labs/valkyrie/issues/new).
2
+
3
+ If you have questions or need help, please email [the Samvera community tech list](https://groups.google.com/forum/#!forum/samvera-tech) or stop by the #dev channel in [the Samvera community Slack team](https://wiki.duraspace.org/pages/viewpage.action?pageId=87460391#Getintouch!-Slack).
4
+
5
+ You can learn more about the various Samvera communication channels on the [Get in touch!](https://wiki.duraspace.org/pages/viewpage.action?pageId=87460391) wiki page.
data/lib/valkyrie.rb CHANGED
@@ -86,13 +86,39 @@ module Valkyrie
86
86
  Valkyrie::StorageAdapter.find(super.to_sym)
87
87
  end
88
88
 
89
+ # @api public
90
+ #
91
+ # The returned anonymous method (e.g. responds to #call) has a signature of
92
+ # an unamed parameter that is a string. Calling the anonymous method should
93
+ # return a Valkyrie::Resource from which Valkyrie will map the persisted
94
+ # data into.
95
+ #
96
+ # @return [#call] with method signature of 1
97
+ #
98
+ # @see #default_resource_class_resolver for full interface
99
+ def resource_class_resolver
100
+ super
101
+ end
102
+
103
+ # @!attribute [w] resource_class_resolver=
104
+ # The setter for #resource_class_resolver; see it's implementation
105
+
89
106
  private
90
107
 
91
108
  def defaults
92
109
  {
93
- standardize_query_result: false
110
+ standardize_query_result: false,
111
+ resource_class_resolver: method(:default_resource_class_resolver)
94
112
  }
95
113
  end
114
+
115
+ # String constantize is a "by convention" factory. This works, but assumes
116
+ # the ruby class once used to persist is the model used to now reify.
117
+ #
118
+ # @param [String] class_name
119
+ def default_resource_class_resolver(class_name)
120
+ class_name.constantize
121
+ end
96
122
  end
97
123
 
98
124
  module_function :config, :logger, :logger=, :config_root_path, :environment, :warn_about_standard_queries!, :config_file, :config_hash
@@ -7,7 +7,13 @@ module Valkyrie::Persistence
7
7
  # persister = Valkyrie.config.metadata_adapter
8
8
  # index_persister = Valkyrie::MetadataAdapter.find(:index_solr)
9
9
  # Valkyrie::MetadataAdapter.register(
10
- # Valkyrie::Persistence::CompositePersister.new(persister, index_persister),
10
+ # Valkyrie::AdapterContainer.new(
11
+ # persister: Valkyrie::Persistence::CompositePersister.new(
12
+ # persister,
13
+ # index_persister
14
+ # )
15
+ # query_service: Valkyrie.config.metadata_adapter.query_service
16
+ # )
11
17
  # :my_composite_persister
12
18
  # )
13
19
  #
@@ -111,8 +111,10 @@ module Valkyrie::Persistence::Fedora
111
111
  # Find all resources referencing a given resource (e. g. parents)
112
112
  # *This is done by iterating through the ID of each resource referencing the resource in the query, and requesting each resource over the HTTP*
113
113
  # *Also, an initial request is made to find the URIs of the resources referencing the resource in the query*
114
- def find_inverse_references_by(resource:, property:)
115
- ensure_persisted(resource)
114
+ def find_inverse_references_by(resource: nil, id: nil, property:)
115
+ raise ArgumentError, "Provide resource or id" unless resource || id
116
+ ensure_persisted(resource) if resource
117
+ resource ||= find_by(id: id)
116
118
  if ordered_property?(resource: resource, property: property)
117
119
  find_inverse_references_by_ordered(resource: resource, property: property)
118
120
  else
@@ -36,7 +36,10 @@ module Valkyrie::Persistence::Memory
36
36
  def find_by_alternate_identifier(alternate_identifier:)
37
37
  alternate_identifier = Valkyrie::ID.new(alternate_identifier.to_s) if alternate_identifier.is_a?(String)
38
38
  validate_id(alternate_identifier)
39
- cache.select { |_key, resource| resource[:alternate_ids].include?(alternate_identifier) }.values.first || raise(::Valkyrie::Persistence::ObjectNotFoundError)
39
+ cache.select do |_key, resource|
40
+ next unless resource[:alternate_ids]
41
+ resource[:alternate_ids].include?(alternate_identifier)
42
+ end.values.first || raise(::Valkyrie::Persistence::ObjectNotFoundError)
40
43
  end
41
44
 
42
45
  # Get a batch of resources by ID.
@@ -110,10 +113,12 @@ module Valkyrie::Persistence::Memory
110
113
  # @return [Array<Valkyrie::Resource>] All resources in the persistence backend
111
114
  # which have the ID of the given `resource` in their `property` property. Not
112
115
  # in order.
113
- def find_inverse_references_by(resource:, property:)
114
- ensure_persisted(resource)
116
+ def find_inverse_references_by(resource: nil, id: nil, property:)
117
+ raise ArgumentError, "Provide resource or id" unless resource || id
118
+ ensure_persisted(resource) if resource
119
+ id ||= resource.id
115
120
  find_all.select do |obj|
116
- Array.wrap(obj[property]).include?(resource.id)
121
+ Array.wrap(obj[property]).include?(id)
117
122
  end
118
123
  end
119
124
 
@@ -1,8 +1,14 @@
1
1
  # frozen_string_literal: true
2
- #
3
2
  module Valkyrie::Persistence
4
3
  # Implements the DataMapper Pattern to store metadata into Postgres
5
4
  module Postgres
5
+ # Deprecation to allow us to make pg an optional dependency
6
+ path = Bundler.definition.gemfiles.first
7
+ matches = File.readlines(path).select { |l| l =~ /gem ['"]pg\b/ }
8
+ if matches.empty?
9
+ warn "[DEPRECATION] pg will not be included as a dependency in Valkyrie's gemspec as of the next major release. Please add the gem directly to your Gemfile if you use a postgres adapter."
10
+ end
11
+
6
12
  require 'valkyrie/persistence/postgres/metadata_adapter'
7
13
  end
8
14
  end
@@ -56,7 +56,7 @@ module Valkyrie::Persistence::Postgres
56
56
  # Retrieve the Class used to construct the Valkyrie Resource
57
57
  # @return [Class]
58
58
  def resource_klass
59
- internal_resource.constantize
59
+ Valkyrie.config.resource_class_resolver.call(internal_resource)
60
60
  end
61
61
 
62
62
  # Access the String for the Valkyrie Resource type within the attributes
@@ -19,7 +19,15 @@ module Valkyrie::Persistence::Postgres
19
19
  # was modified in the database between been read into memory and persisted
20
20
  def save(resource:)
21
21
  orm_object = resource_factory.from_resource(resource: resource)
22
- orm_object.save!
22
+ orm_object.transaction do
23
+ orm_object.save!
24
+ if resource.id && resource.id.to_s != orm_object.id
25
+ warn "[DEPRECATION] Postgres' primary key column can not save with the given ID #{resource.id}. " \
26
+ "For now a new ID has been assigned (#{orm_object.id}), but in the next major version this will throw an exception and not save. " \
27
+ "To avoid this warning, set the ID to be nil via `resource.id = nil` before you save it. \n" \
28
+ "Called from #{Gem.location_of_caller.join(':')}"
29
+ end
30
+ end
23
31
  resource_factory.to_resource(object: orm_object)
24
32
  rescue ActiveRecord::StaleObjectError
25
33
  raise Valkyrie::Persistence::StaleObjectError, "The object #{resource.id} has been updated by another process."
@@ -112,9 +112,11 @@ module Valkyrie::Persistence::Postgres
112
112
  # @param [Valkyrie::Resource] resource
113
113
  # @param [String] property
114
114
  # @return [Array<Valkyrie::Resource>]
115
- def find_inverse_references_by(resource:, property:)
116
- ensure_persisted(resource)
117
- internal_array = "{\"#{property}\": [{\"id\": \"#{resource.id}\"}]}"
115
+ def find_inverse_references_by(resource: nil, id: nil, property:)
116
+ raise ArgumentError, "Provide resource or id" unless resource || id
117
+ ensure_persisted(resource) if resource
118
+ id ||= resource.id
119
+ internal_array = "{\"#{property}\": [{\"id\": \"#{id}\"}]}"
118
120
  run_query(find_inverse_references_query, internal_array)
119
121
  end
120
122
 
@@ -41,16 +41,25 @@ module Valkyrie::Persistence::Solr
41
41
  if resource_attributes[:created_at]
42
42
  DateTime.parse(resource_attributes[:created_at].to_s).utc.iso8601
43
43
  else
44
- Time.current.utc.iso8601
44
+ Time.current.utc.iso8601(6)
45
45
  end
46
46
  end
47
47
 
48
+ # @return [String] ISO-8601 timestamp in UTC of the updated_at for solr
49
+ # @note Solr stores its own updated_at timestamp, but for performance
50
+ # reasons we're generating our own. Without doing so, every time we add a
51
+ # new document we'd have to do a GET to find out the timestamp.
52
+ def updated_at
53
+ Time.current.utc.iso8601(6)
54
+ end
55
+
48
56
  # @return [Hash] Solr document to index.
49
57
  def to_h
50
58
  {
51
59
  "id": id,
52
60
  "join_id_ssi": "id-#{id}",
53
- "created_at_dtsi": created_at
61
+ "created_at_dtsi": created_at,
62
+ "updated_at_dtsi": updated_at
54
63
  }.merge(add_single_values(attribute_hash)).merge(lock_hash)
55
64
  end
56
65
 
@@ -26,7 +26,7 @@ module Valkyrie::Persistence::Solr
26
26
  # Access the Class for the Valkyrie Resource
27
27
  # @return [Class]
28
28
  def resource_klass
29
- internal_resource.constantize
29
+ Valkyrie.config.resource_class_resolver.call(internal_resource)
30
30
  end
31
31
 
32
32
  # Access the String specifying the Valkyrie Resource type in the Solr Document
@@ -54,7 +54,7 @@ module Valkyrie::Persistence::Solr
54
54
  # Construct a Time object from the datestamp for the date of the last resource update indexed in Solr
55
55
  # @return [Time]
56
56
  def updated_at
57
- DateTime.parse(solr_document["timestamp"] || solr_document.fetch("created_at_dtsi").to_s).utc
57
+ DateTime.parse(solr_document.fetch("updated_at_dtsi").to_s || solr_document["timestamp"] || solr_document.fetch("created_at_dtsi").to_s).utc
58
58
  end
59
59
 
60
60
  # Construct the OptimisticLockToken object using the "_version_" field value in the Solr Document
@@ -3,14 +3,14 @@ module Valkyrie::Persistence::Solr::Queries
3
3
  # Responsible for efficiently returning all {Valkyrie::Resource}s which
4
4
  # reference a {Valkyrie::Resource} in a given property.
5
5
  class FindInverseReferencesQuery
6
- attr_reader :resource, :property, :connection, :resource_factory
6
+ attr_reader :id, :property, :connection, :resource_factory
7
7
 
8
8
  # @param [Valkyrie::Resource] resource
9
9
  # @param [String] property
10
10
  # @param [RSolr::Client] connection
11
11
  # @param [ResourceFactory] resource_factory
12
- def initialize(resource:, property:, connection:, resource_factory:)
13
- @resource = resource
12
+ def initialize(resource: nil, id: nil, property:, connection:, resource_factory:)
13
+ @id = id ? id : resource.id
14
14
  @property = property
15
15
  @connection = connection
16
16
  @resource_factory = resource_factory
@@ -39,7 +39,7 @@ module Valkyrie::Persistence::Solr::Queries
39
39
  # @note the field used here is a _ssim dynamic field and the value is prefixed by "id-"
40
40
  # @return [Hash]
41
41
  def query
42
- "#{property}_ssim:id-#{resource.id}"
42
+ "#{property}_ssim:id-#{id}"
43
43
  end
44
44
  end
45
45
  end
@@ -92,9 +92,11 @@ module Valkyrie::Persistence::Solr
92
92
  # @param [Valkyrie::Resource] referenced resource
93
93
  # @param [Symbol, String] property
94
94
  # @return [Array<Valkyrie::Resource>] related resources
95
- def find_inverse_references_by(resource:, property:)
96
- ensure_persisted(resource)
97
- Valkyrie::Persistence::Solr::Queries::FindInverseReferencesQuery.new(resource: resource, property: property, connection: connection, resource_factory: resource_factory).run
95
+ def find_inverse_references_by(resource: nil, id: nil, property:)
96
+ raise ArgumentError, "Provide resource or id" unless resource || id
97
+ ensure_persisted(resource) if resource
98
+ id ||= resource.id
99
+ Valkyrie::Persistence::Solr::Queries::FindInverseReferencesQuery.new(id: id, property: property, connection: connection, resource_factory: resource_factory).run
98
100
  end
99
101
 
100
102
  # Construct the Valkyrie::Persistence::CustomQueryContainer object using this query service
@@ -2,6 +2,7 @@
2
2
  RSpec.shared_examples 'a Valkyrie::Persister' do |*flags|
3
3
  before do
4
4
  raise 'persister must be set with `let(:persister)`' unless defined? persister
5
+ raise 'query_service must be set with `let(:query_service)`' unless defined? query_service
5
6
  class CustomResource < Valkyrie::Resource
6
7
  include Valkyrie::Resource::AccessControls
7
8
  attribute :title
@@ -92,6 +93,7 @@ RSpec.shared_examples 'a Valkyrie::Persister' do |*flags|
92
93
  expect(book.updated_at).not_to be_blank
93
94
  expect(book.created_at).not_to be_kind_of Array
94
95
  expect(book.updated_at).not_to be_kind_of Array
96
+ expect(book.updated_at > book.created_at).to eq true
95
97
  end
96
98
 
97
99
  it "can handle Boolean RDF properties" do
@@ -235,7 +237,7 @@ RSpec.shared_examples 'a Valkyrie::Persister' do |*flags|
235
237
  end
236
238
 
237
239
  it "can store Valkyrie::IDs" do
238
- shared_title = persister.save(resource: resource_class.new(id: "test"))
240
+ shared_title = persister.save(resource: resource_class.new)
239
241
  book = persister.save(resource: resource_class.new(title: [shared_title.id, Valkyrie::ID.new("adapter://1"), "test"]))
240
242
  reloaded = query_service.find_by(id: book.id)
241
243
  expect(reloaded.title).to contain_exactly(shared_title.id, Valkyrie::ID.new("adapter://1"), "test")
@@ -4,7 +4,7 @@ RSpec.shared_examples 'a Valkyrie query provider' do
4
4
  raise 'adapter must be set with `let(:adapter)`' unless
5
5
  defined? adapter
6
6
  class CustomResource < Valkyrie::Resource
7
- attribute :alternate_ids, Valkyrie::Types::Array
7
+ attribute :alternate_ids, Valkyrie::Types::Set.of(Valkyrie::Types::ID)
8
8
  attribute :title
9
9
  attribute :member_ids, Valkyrie::Types::Array
10
10
  attribute :a_member_of, Valkyrie::Types::Array
@@ -32,6 +32,7 @@ RSpec.shared_examples 'a Valkyrie query provider' do
32
32
  it { is_expected.to respond_to(:find_members).with_keywords(:resource, :model) }
33
33
  it { is_expected.to respond_to(:find_references_by).with_keywords(:resource, :property) }
34
34
  it { is_expected.to respond_to(:find_inverse_references_by).with_keywords(:resource, :property) }
35
+ it { is_expected.to respond_to(:find_inverse_references_by).with_keywords(:id, :property) }
35
36
  it { is_expected.to respond_to(:find_parents).with_keywords(:resource) }
36
37
 
37
38
  describe ".find_all" do
@@ -92,7 +93,12 @@ RSpec.shared_examples 'a Valkyrie query provider' do
92
93
  expect(found).to be_persisted
93
94
  end
94
95
 
95
- it "returns a Valkyrie::Persistence::ObjectNotFoundError for a non-found alternate identifier" do
96
+ it 'raises a Valkyrie::Persistence::ObjectNotFoundError when persisted objects do not have alternate_ids' do
97
+ persister.save(resource: SecondResource.new)
98
+ expect { query_service.find_by_alternate_identifier(alternate_identifier: Valkyrie::ID.new("123123123")) }.to raise_error ::Valkyrie::Persistence::ObjectNotFoundError
99
+ end
100
+
101
+ it "raises a Valkyrie::Persistence::ObjectNotFoundError for a non-found alternate identifier" do
96
102
  expect { query_service.find_by_alternate_identifier(alternate_identifier: Valkyrie::ID.new("123123123")) }.to raise_error ::Valkyrie::Persistence::ObjectNotFoundError
97
103
  end
98
104
 
@@ -304,6 +310,26 @@ RSpec.shared_examples 'a Valkyrie query provider' do
304
310
  end
305
311
  end
306
312
  end
313
+
314
+ context "when id is passed instead of resource" do
315
+ it "returns everything which references the given resource by the given property" do
316
+ parent = persister.save(resource: resource_class.new)
317
+ parent2 = persister.save(resource: resource_class.new)
318
+ child = persister.save(resource: resource_class.new(a_member_of: [parent.id]))
319
+ child2 = persister.save(resource: resource_class.new(a_member_of: [parent.id, parent2.id, parent.id]))
320
+ persister.save(resource: resource_class.new)
321
+ persister.save(resource: SecondResource.new)
322
+
323
+ expect(query_service.find_inverse_references_by(id: parent.id, property: :a_member_of).map(&:id).to_a).to contain_exactly child.id, child2.id
324
+ end
325
+ end
326
+
327
+ context "when neither id nor resource is passed" do
328
+ it "raises an error" do
329
+ expect { query_service.find_inverse_references_by(property: :a_member_of) }.to raise_error ArgumentError
330
+ end
331
+ end
332
+
307
333
  context "when the resource is not saved" do
308
334
  it "raises an error" do
309
335
  parent = resource_class.new
@@ -41,7 +41,8 @@ module Valkyrie::Storage
41
41
  request.headers['Content-Disposition'] = "attachment; filename=\"#{original_filename}\""
42
42
  request.headers['digest'] = "#{sha1}=#{Digest::SHA1.file(file)}"
43
43
  request.headers['link'] = "<http://www.w3.org/ns/ldp#NonRDFSource>; rel=\"type\""
44
- request.body = file.tempfile.read
44
+ io = Faraday::UploadIO.new(file.tempfile.path, file.content_type)
45
+ request.body = io
45
46
  end
46
47
  find_by(id: Valkyrie::ID.new(identifier.to_s.sub(/^.+\/\//, PROTOCOL)))
47
48
  end
@@ -29,6 +29,16 @@ module Valkyrie
29
29
  end
30
30
  end
31
31
 
32
+ module Params
33
+ ID = Valkyrie::Types::ID.constructor do |input|
34
+ if input.blank?
35
+ nil
36
+ else
37
+ Valkyrie::Types::ID[input]
38
+ end
39
+ end
40
+ end
41
+
32
42
  # Valkyrie::URI
33
43
  URI = Dry::Types::Definition
34
44
  .new(RDF::URI)
@@ -51,7 +61,8 @@ module Valkyrie
51
61
  # Used for casting {Valkyrie::Resources} if possible.
52
62
  Anything = Valkyrie::Types::Any.constructor do |value|
53
63
  if value.respond_to?(:fetch) && value.fetch(:internal_resource, nil)
54
- value.fetch(:internal_resource).constantize.new(value)
64
+ resource_klass = Valkyrie.config.resource_class_resolver.call(value.fetch(:internal_resource))
65
+ resource_klass.new(value)
55
66
  else
56
67
  value
57
68
  end
@@ -87,6 +98,10 @@ module Valkyrie
87
98
  end
88
99
 
89
100
  def member(type)
101
+ warn "[DEPRECATION] .member has been renamed to .of in dry-types and this " \
102
+ "method will be removed in the next major version of Valkyrie. Please use " \
103
+ ".of instead. " \
104
+ "Called from #{Gem.location_of_caller.join(':')}"
90
105
  super.default([].freeze)
91
106
  end
92
107
  end
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module Valkyrie
3
- VERSION = "1.4.0"
3
+ VERSION = "1.5.0.RC1"
4
4
  end
data/valkyrie.gemspec CHANGED
@@ -35,7 +35,7 @@ Gem::Specification.new do |spec|
35
35
  spec.add_dependency 'json'
36
36
  spec.add_dependency 'active-triples'
37
37
 
38
- spec.add_development_dependency "bundler", "~> 1.13"
38
+ spec.add_development_dependency "bundler", "~> 1.16"
39
39
  spec.add_development_dependency "rake", "~> 10.0"
40
40
  spec.add_development_dependency "rspec", "~> 3.0"
41
41
  spec.add_development_dependency "pry"
@@ -50,4 +50,5 @@ Gem::Specification.new do |spec|
50
50
  spec.add_development_dependency 'fcrepo_wrapper'
51
51
  spec.add_development_dependency 'docker-stack', '~> 0.2.6'
52
52
  spec.add_development_dependency 'activerecord', '~> 5.1.0'
53
+ spec.add_development_dependency 'timecop'
53
54
  end
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: 1.4.0
4
+ version: 1.5.0.RC1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Trey Pendragon
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-01-09 00:00:00.000000000 Z
11
+ date: 2019-02-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dry-struct
@@ -226,14 +226,14 @@ dependencies:
226
226
  requirements:
227
227
  - - "~>"
228
228
  - !ruby/object:Gem::Version
229
- version: '1.13'
229
+ version: '1.16'
230
230
  type: :development
231
231
  prerelease: false
232
232
  version_requirements: !ruby/object:Gem::Requirement
233
233
  requirements:
234
234
  - - "~>"
235
235
  - !ruby/object:Gem::Version
236
- version: '1.13'
236
+ version: '1.16'
237
237
  - !ruby/object:Gem::Dependency
238
238
  name: rake
239
239
  requirement: !ruby/object:Gem::Requirement
@@ -430,6 +430,20 @@ dependencies:
430
430
  - - "~>"
431
431
  - !ruby/object:Gem::Version
432
432
  version: 5.1.0
433
+ - !ruby/object:Gem::Dependency
434
+ name: timecop
435
+ requirement: !ruby/object:Gem::Requirement
436
+ requirements:
437
+ - - ">="
438
+ - !ruby/object:Gem::Version
439
+ version: '0'
440
+ type: :development
441
+ prerelease: false
442
+ version_requirements: !ruby/object:Gem::Requirement
443
+ requirements:
444
+ - - ">="
445
+ - !ruby/object:Gem::Version
446
+ version: '0'
433
447
  description:
434
448
  email:
435
449
  - tpendragon@princeton.edu
@@ -446,10 +460,13 @@ files:
446
460
  - ".rubocop.yml"
447
461
  - ".rubocop_todo.yml"
448
462
  - CHANGELOG.md
463
+ - CODE_OF_CONDUCT.md
464
+ - CONTRIBUTING.md
449
465
  - Gemfile
450
466
  - LICENSE
451
467
  - README.md
452
468
  - Rakefile
469
+ - SUPPORT.md
453
470
  - bin/console
454
471
  - bin/jetty_wait
455
472
  - bin/rspec
@@ -585,12 +602,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
585
602
  version: '0'
586
603
  required_rubygems_version: !ruby/object:Gem::Requirement
587
604
  requirements:
588
- - - ">="
605
+ - - ">"
589
606
  - !ruby/object:Gem::Version
590
- version: '0'
607
+ version: 1.3.1
591
608
  requirements: []
592
609
  rubyforge_project:
593
- rubygems_version: 2.7.7
610
+ rubygems_version: 2.6.14
594
611
  signing_key:
595
612
  specification_version: 4
596
613
  summary: An ORM using the Data Mapper pattern, specifically built to solve Digital