-
Notifications
You must be signed in to change notification settings - Fork 23
Saving a resource
- Understand persistence.
- Create and save a Book resource.
- Inspect the database after persisting a resource.
- Create and save a Page as a member of a Book.
- Create and save CoverArt associated with a Book and observe its persistence before and after saving the Book.
One of the biggest conceptual differences between the Active Record and Data Mapper patterns is that while an Active Record object knows how to save itself, a Data Mapper object does not. Instead, a Data Mapper application can implement multiple persisters. Each persister accepts an object as an argument and saves it to persistent storage. The storage systems that are easily configured for Valkyrie so far include Postgres and Fedora.
One of the advantages of this pattern is that it provides an abstraction layer between an object in memory and the way it is persisted, whether that is to a database, a triple store, or some other system.
Reference: Valkyrie Persistence Documentation | postgres persister | shared specs/persister
Terminology
- persister - provides methods for saving resources
Characteristics
- resources are passed to the persister
- code example of a postgres persister
- code example of shared specs for persisters tests demonstrating expected function of persister methods
Run the following in bundle exec rails console
to save a new book resource...
If you drop in and out of rails console, you will have to recreate some of the objects in this lesson to proceed. Also, you may see more rows in the table than described if you need to persist a resource multiple times. |
> book = Book.new(alternate_ids: 'b1', title: 'Free Fall', author: 'Robert Crais', series: 'Elvis Cole/Joe Pike')
> saved_book = ValkyriePgDemo.pg_persister.save(resource: book)
Note that the book
object has not changed. In particular, it does not have values assigned for id
, created_at
, or persisted?
.
> book.alternate_ids
=> [#<Valkyrie::ID:... @id="b1"]
> book.id
=> nil
> book.created_at
=> nil
> book.persisted?
=> false
Note that the saved_book
object now contains all of the attributes of the book
object, plus the changes made to an object when it is persisted.
> saved_book.alternate_ids
=> [#<Valkyrie::ID:... @id="b1">]
> saved_book.id
=> #<Valkyrie::ID:... @id="_AN_ID_ASSIGNED_BY_POSTGRES_">
> saved_book.created_at
=> Mon, 09 Dec 2019 19:01:04 UTC +00:00
> saved_book.persisted?
=> true
> saved_book.title
=> ["Free Fall"]
Record the value of saved_book.id.to_s
. You will use this in Retrieving resources.
Valkyrie only adds one table to the postgres database: orm_resources
. In the terminal, open the postgres command line with psql postgres
. NOTE: To quit the postgres command line, type \q
.
postgres=# \c valkyrie_pg_demo_development
postgres=# \dt
List of relations
Schema | Name | Type | Owner
--------+----------------------+-------+---------------
public | ar_internal_metadata | table | valkyrie_user
public | orm_resources | table | valkyrie_user
public | schema_migrations | table | valkyrie_user
(3 rows)
Saving book
creates a single row in table orm_resources
in the postgres database. The metadata
column holds the resource's data as json
and the internal_resource
column holds the model (e.g. 'Book'
).
postgres=# select * from orm_resources;
id | metadata | created_at | updated_at | internal_resource | lock_version
--------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------+----------------------------+-------------------+--------------
c1a6dacf-a41f-4a04-ab71-af2ebf090e23 | {"title": ["Free Fall"], "author": ["Robert Crais"], "series": ["Elvis Cole/Joe Pike"], "cover_art": [], "new_record": [true], "alternate_ids": [{"id": "b1"}]} | 2019-12-18 17:06:30.192841 | 2019-12-18 17:06:30.192841 | Book |
In general, attributes without values are not saved in the metadata json. In this case, member_ids was not included. Arrays and Sets are an empty array if they do not have values. |
IDs are examples only. Record the actual id after saving using saved_book.id.to_s for use in Retrieving resources. |
Recall from Creating relationships between resources, it was stated that best practice is to not assign an id prior to saving and to let the datastore create the id during the persistence process. Try assigning one here to see what happens in postgres if you try to assign an id.
> book2 = Book.new(id: 'book_2', alternate_ids: 'b2', title: 'Lullaby Town', author: 'Robert Crais', series: 'Elvis Cole/Joe Pike')
> ValkyriePgDemo.pg_persister.save(resource: book2)
=> Valkyrie::Persistence::UnsupportedDatatype
Postgres' primary key column cannot save with the given ID book_2. To avoid this error, set the ID to be nil via resource.id = nil
before you save it.
Run the following in bundle exec rails console
to save a new page resource and update the book resource. Note that saved_book
was created in a previous example:
> book = saved_book
> page = Page.new(page_num: '1', structure: 'title page')
> page = ValkyriePgDemo.pg_persister.save(resource: page)
# saves the new page resource
> book.member_ids = page.id
> book = ValkyriePgDemo.pg_persister.save(resource: book)
# updates the existing book resource
Saving page
creates a second row in the table orm_resources
with the page data in the metadata
column and internal_resource=Page
. Saving the book
resource updates its metadata
to add the member_ids
.
In the postgres command line:
postgres=# \c valkyrie_pg_demo_development
postgres=# select * from orm_resources;
id | metadata | created_at | updated_at | internal_resource | lock_version
--------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------+----------------------------+-------------------+--------------
844c5e0e-8a27-48c6-b69b-750e623f5290 | {"page_num": ["1"], "structure": ["title page"], "new_record": [true], "alternate_ids": []} | 2019-12-18 17:07:31.577452 | 2019-12-18 17:07:31.577452 | Page |
c1a6dacf-a41f-4a04-ab71-af2ebf090e23 | {"title": ["Free Fall"], "author": ["Robert Crais"], "series": ["Elvis Cole/Joe Pike"], "cover_art": [], "member_ids": [{"id": "844c5e0e-8a27-48c6-b69b-750e623f5290"}], "new_record": [false], "alternate_ids": [{"id": "b1"}]} | 2019-12-18 17:06:30.192841 | 2019-12-18 17:08:04.411385 | Book |
Run the following in bundle exec rails console
to create a CoverArt resource in the Book resource and save the updated book:
> book.cover_art = CoverArt.new(artists: 'Mary GrandPré')
> book = ValkyriePgDemo.pg_persister.save(resource: book)
# updates the existing book resource
This does not add a new row in the table orm_resources
, because cover_art
has not been persisted in its own right; however, the metadata pertaining to cover_art
has been added to the book
resource. Saving the book
resource updates its metadata
to add the cover_art
with the full cover_art resource data.
In postgres command line:
postgres=# \c valkyrie_pg_demo_development
postgres=# select * from orm_resources;
id | metadata | created_at | updated_at | internal_resource | lock_version
--------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------+----------------------------+-------------------+--------------
844c5e0e-8a27-48c6-b69b-750e623f5290 | {"page_num": ["1"], "structure": ["title page"], "new_record": [true], "alternate_ids": []} | 2019-12-18 17:07:31.577452 | 2019-12-18 17:07:31.577452 | Page |
c1a6dacf-a41f-4a04-ab71-af2ebf090e23 | {"title": ["Free Fall"], "author": ["Robert Crais"], "series": ["Elvis Cole/Joe Pike"], "cover_art": [{"id": null, "artists": ["Mary GrandPré"], "image_id": null, "created_at": null, "new_record": true, "updated_at": null, "alternate_ids": [], "internal_resource": "CoverArt"}], "member_ids": [{"id": "844c5e0e-8a27-48c6-b69b-750e623f5290"}], "new_record": [false], "alternate_ids": [{"id": "b1"}]} | 2019-12-18 17:06:30.192841 | 2019-12-18 17:11:20.672987 | Book |