atlas_rb 1.16.1 → 1.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.version +1 -1
- data/CHANGELOG.md +56 -0
- data/Gemfile.lock +1 -1
- data/README.md +36 -0
- data/lib/atlas_rb/resource.rb +18 -2
- data/lib/atlas_rb/resource_types.rb +60 -0
- data/lib/atlas_rb.rb +2 -0
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3a418fc64c12474c618d8bcffbaf01f116f0bb7c80c999f4c3bd4514cf47c879
|
|
4
|
+
data.tar.gz: ebfa9f0d7e85b884f6f96a16dd59861d01a0d03bbf6a25c94a0e92665add8d2d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1e2541b872b918215dd73155ae55fc8d841a7da7fce77f6b984dc82187148dce0a4698ee2b8aaa561b659d1fd253f2077f614cb8d592edaa54a10a343ea8664a
|
|
7
|
+
data.tar.gz: c0bb9652d3a1cfe08485d6057f23db0e7c493fefe4bfd23be7796e825e1a0bc998b4b02e625c2475e87b247ffc49ffb229469c35adb3b770010bda4cb42c1220
|
data/.version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
1.
|
|
1
|
+
1.17.0
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,61 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.17.0
|
|
4
|
+
|
|
5
|
+
### Fixed — `Resource.find` emitted a type string this namespace cannot resolve
|
|
6
|
+
|
|
7
|
+
`find` derived its `"klass"` with `String#capitalize` over Atlas's JSON key,
|
|
8
|
+
and `capitalize` is not the inverse of a Ruby class name once the type has two
|
|
9
|
+
words. Atlas answers `{"file_set": {...}}`, and `"file_set".capitalize` is
|
|
10
|
+
`"File_set"`. There is no `AtlasRb::File_set`.
|
|
11
|
+
|
|
12
|
+
The doc comment stated the convention as a promise — *"the resource type,
|
|
13
|
+
capitalized (e.g. `"Work"`)"* — and `"Work"` is the one shape where
|
|
14
|
+
capitalizing happens to work. Every other consumer read that as "`const_get`
|
|
15
|
+
this", so a FileSet NOID put a `NameError` in the caller's stack:
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
uninitialized constant AtlasRb::File_set
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
`find` now reports the class's own name, so a FileSet is `"FileSet"`.
|
|
22
|
+
|
|
23
|
+
**This changes one value on the wire**: `"File_set"` becomes `"FileSet"`. That
|
|
24
|
+
is the spelling Solr already carries as `internal_resource`, so a consumer fed
|
|
25
|
+
by both sources now sees one vocabulary instead of two.
|
|
26
|
+
|
|
27
|
+
### Added — `Resource.class_for`, the supported type-string → class lookup
|
|
28
|
+
|
|
29
|
+
```ruby
|
|
30
|
+
AtlasRb::Resource.class_for("file_set") # => AtlasRb::FileSet
|
|
31
|
+
AtlasRb::Resource.class_for("FileSet") # => AtlasRb::FileSet
|
|
32
|
+
AtlasRb::Resource.class_for("File_set") # => AtlasRb::FileSet
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Closing the loop on `find` used to mean reaching into this namespace by string
|
|
36
|
+
on a naming convention the gem never promised. `class_for` reads a stated,
|
|
37
|
+
closed set — `Resource::TYPE_MAP`, covering the eight `Resource` subclasses —
|
|
38
|
+
and accepts all three spellings the DRS stack produces for a type: Atlas's
|
|
39
|
+
wire key, Solr's `internal_resource`, and the pre-1.17.0 `"klass"` a caller
|
|
40
|
+
can still be holding.
|
|
41
|
+
|
|
42
|
+
The set is stated rather than derived because there is no rule to derive:
|
|
43
|
+
`Blob`'s `ROUTE` is `/files/`.
|
|
44
|
+
|
|
45
|
+
An unrecognized type raises `ArgumentError` — never `nil`, and never
|
|
46
|
+
`const_get`, which would resolve an arbitrary constant in this namespace. Same
|
|
47
|
+
argument `Middleware::RaiseOnReadError` settled for the read path: keep the
|
|
48
|
+
failure where the cause is.
|
|
49
|
+
|
|
50
|
+
### Documented — what the generic resolver actually covers
|
|
51
|
+
|
|
52
|
+
Atlas answers `/resources/:id` for its Valkyrie-backed types only: `Work`,
|
|
53
|
+
`Collection`, `Community`, `FileSet`, `Blob`, `Delegate` and `Person`. A
|
|
54
|
+
`Compilation` is an ActiveRecord row in Atlas rather than a Valkyrie resource,
|
|
55
|
+
so the resolver never finds one, though `Compilation.find` does. `nil` from
|
|
56
|
+
`Resource.find` is therefore ambiguous: it means "no such id" **or** "that id
|
|
57
|
+
names a Compilation". `Resource.find`'s YARD and the README both say so now.
|
|
58
|
+
|
|
3
59
|
## 1.16.0
|
|
4
60
|
|
|
5
61
|
### Fixed — every read binding consults the HTTP status before it reads the body
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
|
@@ -531,6 +531,42 @@ The two mutations raise the same way `reparent` does — `LinkedMemberError`
|
|
|
531
531
|
on a structural `422` (carrying the envelope's `error` code as `#code`) and
|
|
532
532
|
`ForbiddenError` on a `403` — instead of swallowing the envelope.
|
|
533
533
|
|
|
534
|
+
### Resolving one id of unknown type (`Resource.find` and `Resource.class_for`)
|
|
535
|
+
|
|
536
|
+
When a NOID arrives as runtime data and you do not know its type, resolve
|
|
537
|
+
it generically and dispatch on the reported type:
|
|
538
|
+
|
|
539
|
+
```ruby
|
|
540
|
+
found = AtlasRb::Resource.find("b8gtjvk")
|
|
541
|
+
found["klass"] # => "FileSet"
|
|
542
|
+
klass = AtlasRb::Resource.class_for(found["klass"]) # => AtlasRb::FileSet
|
|
543
|
+
klass.find(found["resource"]["id"])
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
`class_for` is the supported way to turn a type string into a class. Do not
|
|
547
|
+
`const_get` into the `AtlasRb` namespace: the mapping is a stated, closed
|
|
548
|
+
set (`Resource::TYPE_MAP`), not a naming rule — `Blob`'s route is `/files/`,
|
|
549
|
+
and a wire key like `file_set` is not the class name.
|
|
550
|
+
|
|
551
|
+
It accepts every spelling the DRS stack produces for a type, because a
|
|
552
|
+
caller cannot tell which one it is holding:
|
|
553
|
+
|
|
554
|
+
| Spelling | Where it comes from |
|
|
555
|
+
|---------------|--------------------------------------------|
|
|
556
|
+
| `"file_set"` | Atlas's JSON wire key |
|
|
557
|
+
| `"FileSet"` | Solr's `internal_resource`, and `find`'s `"klass"` |
|
|
558
|
+
| `"File_set"` | a `"klass"` read from a gem before 1.17.0 |
|
|
559
|
+
|
|
560
|
+
An unknown type raises `ArgumentError` rather than returning `nil`, so a
|
|
561
|
+
type this gem does not model fails where the cause is.
|
|
562
|
+
|
|
563
|
+
**What the resolver covers.** Atlas answers `/resources/:id` for its
|
|
564
|
+
Valkyrie-backed types only: `Work`, `Collection`, `Community`, `FileSet`,
|
|
565
|
+
`Blob`, `Delegate` and `Person`. A `Compilation` is an ActiveRecord row in
|
|
566
|
+
Atlas rather than a Valkyrie resource, so the generic resolver never finds
|
|
567
|
+
one — use `AtlasRb::Compilation.find` for those. That makes `nil` ambiguous
|
|
568
|
+
here: it means "no such id" **or** "that id names a Compilation".
|
|
569
|
+
|
|
534
570
|
### Batch resolve (`Resource.find_many`)
|
|
535
571
|
|
|
536
572
|
When you have a *set* of NOIDs and only need each one's title / klass /
|
data/lib/atlas_rb/resource.rb
CHANGED
|
@@ -25,6 +25,15 @@ module AtlasRb
|
|
|
25
25
|
# method splits that into a normalized `{ "klass" => ..., "resource" => ... }`
|
|
26
26
|
# pair so callers can dispatch on type.
|
|
27
27
|
#
|
|
28
|
+
# ## What the resolver covers
|
|
29
|
+
#
|
|
30
|
+
# Atlas answers `/resources/:id` for its Valkyrie-backed types only:
|
|
31
|
+
# {Work}, {Collection}, {Community}, {FileSet}, {Blob}, {Delegate} and
|
|
32
|
+
# {Person}. A {Compilation} is an ActiveRecord row in Atlas rather than a
|
|
33
|
+
# Valkyrie resource, so the resolver never finds one — use
|
|
34
|
+
# {Compilation.find} for those. That makes `nil` ambiguous: it means "no
|
|
35
|
+
# such id" **or** "that id names a Compilation".
|
|
36
|
+
#
|
|
28
37
|
# @param id [String] an Atlas resource ID of any type.
|
|
29
38
|
# @param nuid [String, nil] optional acting user's NUID. On the relay-signing
|
|
30
39
|
# path it is signed into the assertion `sub`; on the BYO-JWT (`ATLAS_JWT`)
|
|
@@ -34,10 +43,14 @@ module AtlasRb
|
|
|
34
43
|
# omitted.
|
|
35
44
|
# @return [Hash{String => String, Hash}, nil] hash with two keys, or `nil`
|
|
36
45
|
# when the id resolves to nothing (`404`):
|
|
37
|
-
# - `"klass"` — the resource type,
|
|
46
|
+
# - `"klass"` — the resource type as its class name, e.g. `"Work"`,
|
|
47
|
+
# `"FileSet"`. That is the spelling Solr carries as
|
|
48
|
+
# `internal_resource`, and {Resource.class_for} turns it into the class.
|
|
38
49
|
# - `"resource"` — the resource payload as a Hash.
|
|
39
50
|
# @raise [AtlasRb::ResourceError] on any non-2xx other than `404` / `410` (e.g. an
|
|
40
51
|
# auth/validation error envelope), carrying Atlas's status + body.
|
|
52
|
+
# @raise [ArgumentError] when Atlas names a type this gem defines no class
|
|
53
|
+
# for. See {Resource.class_for}.
|
|
41
54
|
#
|
|
42
55
|
# @example Polymorphic lookup
|
|
43
56
|
# AtlasRb::Resource.find("abc123")
|
|
@@ -46,7 +59,10 @@ module AtlasRb
|
|
|
46
59
|
result = fetch_resource('/resources/' + id, nuid: nuid, on_behalf_of: on_behalf_of)
|
|
47
60
|
return nil if result.nil?
|
|
48
61
|
|
|
49
|
-
|
|
62
|
+
# The class's own name, never `capitalize` over the wire key: `capitalize`
|
|
63
|
+
# answers `"File_set"` for a `file_set`, which is not a constant in this
|
|
64
|
+
# namespace, and callers resolve this string to a class.
|
|
65
|
+
AtlasRb::Mash.new("klass" => class_for(result.first[0]).name.split("::").last,
|
|
50
66
|
"resource" => result.first[1])
|
|
51
67
|
end
|
|
52
68
|
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module AtlasRb
|
|
4
|
+
# Reopens {Resource} with the resource-type vocabulary. This lives in its own
|
|
5
|
+
# file, required after the eight subclasses, because the map holds the classes
|
|
6
|
+
# themselves and every one of them subclasses {Resource} — so none of them is
|
|
7
|
+
# defined yet while `resource.rb` is loading.
|
|
8
|
+
class Resource
|
|
9
|
+
# Every type the Atlas resolver can answer with, keyed by each spelling the
|
|
10
|
+
# DRS stack produces for it: Atlas's wire key (`"file_set"`), the Ruby class
|
|
11
|
+
# name Solr indexes as `internal_resource` (`"FileSet"`), and the
|
|
12
|
+
# capitalize-of-the-wire-key form (`"File_set"`) that a caller can still be
|
|
13
|
+
# holding from a value this gem emitted before {Resource.class_for} existed.
|
|
14
|
+
# A caller cannot tell which of the three it holds, so all three resolve.
|
|
15
|
+
#
|
|
16
|
+
# Stated rather than derived: {Blob}'s `ROUTE` is `/files/`, so nothing in
|
|
17
|
+
# the routes turns a type into its class either.
|
|
18
|
+
TYPE_MAP = {
|
|
19
|
+
"work" => Work, "Work" => Work,
|
|
20
|
+
"collection" => Collection, "Collection" => Collection,
|
|
21
|
+
"community" => Community, "Community" => Community,
|
|
22
|
+
"compilation" => Compilation, "Compilation" => Compilation,
|
|
23
|
+
"file_set" => FileSet, "FileSet" => FileSet, "File_set" => FileSet,
|
|
24
|
+
"blob" => Blob, "Blob" => Blob,
|
|
25
|
+
"delegate" => Delegate, "Delegate" => Delegate,
|
|
26
|
+
"person" => Person, "Person" => Person
|
|
27
|
+
}.freeze
|
|
28
|
+
|
|
29
|
+
# Resolve a resource-type string to the class that models it.
|
|
30
|
+
#
|
|
31
|
+
# Use this on any type that arrives as runtime data — {Resource.find}'s
|
|
32
|
+
# `"klass"`, a Solr `internal_resource` value, or Atlas's wire key — rather
|
|
33
|
+
# than reaching into this namespace with `const_get`. The set is closed and
|
|
34
|
+
# stated in {TYPE_MAP}; it is not a naming rule.
|
|
35
|
+
#
|
|
36
|
+
# An unrecognized type raises instead of resolving to `nil` or to whatever
|
|
37
|
+
# constant happens to bear that name. A caller holding a type this gem does
|
|
38
|
+
# not define has to hear about it here, where the cause is, rather than at
|
|
39
|
+
# the `NoMethodError` a few frames later.
|
|
40
|
+
#
|
|
41
|
+
# @param name [String, Symbol] a resource type in any of the three
|
|
42
|
+
# spellings {TYPE_MAP} accepts.
|
|
43
|
+
# @return [Class] the AtlasRb class for that type.
|
|
44
|
+
# @raise [ArgumentError] when the gem defines no class for that type.
|
|
45
|
+
#
|
|
46
|
+
# @example Dispatching on a type that arrives as data
|
|
47
|
+
# found = AtlasRb::Resource.find("b8gtjvk")
|
|
48
|
+
# AtlasRb::Resource.class_for(found["klass"]).find(found["resource"]["id"])
|
|
49
|
+
#
|
|
50
|
+
# @example Every spelling of one type
|
|
51
|
+
# AtlasRb::Resource.class_for("file_set") # => AtlasRb::FileSet
|
|
52
|
+
# AtlasRb::Resource.class_for("FileSet") # => AtlasRb::FileSet
|
|
53
|
+
# AtlasRb::Resource.class_for("File_set") # => AtlasRb::FileSet
|
|
54
|
+
def self.class_for(name)
|
|
55
|
+
TYPE_MAP.fetch(name.to_s) do
|
|
56
|
+
raise ArgumentError, "unknown Atlas resource type: #{name.inspect}"
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
data/lib/atlas_rb.rb
CHANGED
|
@@ -33,6 +33,8 @@ require_relative "atlas_rb/blob"
|
|
|
33
33
|
require_relative "atlas_rb/delegate"
|
|
34
34
|
require_relative "atlas_rb/compilation"
|
|
35
35
|
require_relative "atlas_rb/person"
|
|
36
|
+
# Must follow every Resource subclass above: it names them in a map.
|
|
37
|
+
require_relative "atlas_rb/resource_types"
|
|
36
38
|
require_relative "atlas_rb/user"
|
|
37
39
|
require_relative "atlas_rb/admin"
|
|
38
40
|
require_relative "atlas_rb/admin/work"
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: atlas_rb
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.17.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- David Cliff
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-09-
|
|
11
|
+
date: 2026-09-17 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: faraday
|
|
@@ -180,6 +180,7 @@ files:
|
|
|
180
180
|
- lib/atlas_rb/middleware/raise_on_stale_resource.rb
|
|
181
181
|
- lib/atlas_rb/person.rb
|
|
182
182
|
- lib/atlas_rb/resource.rb
|
|
183
|
+
- lib/atlas_rb/resource_types.rb
|
|
183
184
|
- lib/atlas_rb/system.rb
|
|
184
185
|
- lib/atlas_rb/system/token.rb
|
|
185
186
|
- lib/atlas_rb/system/user.rb
|