Skip to content

Instantly share code, notes, and snippets.

@wycats
Created June 22, 2012 21:58
Show Gist options
  • Save wycats/2975443 to your computer and use it in GitHub Desktop.
Save wycats/2975443 to your computer and use it in GitHub Desktop.

Relationship Semantics

The new relationship semantics change the current app/store/transaction/adapter APIs in several ways:

Changes to the Application API

At present, a record can be freely moved across transactions as long as it has no changes. Once a record accumulates at least one change, an application cannot move it out of its current transaction until the transaction is committed or rolled back.

Currently, when an application places a child record into a hasMany relationship, the relationship add a foreign key to the child, making it dirty. This means that relational semantics are hardcoded into the ember-data relationship model (child records become dirty because they have a dirty foreign key attribute).

Now, adding a child record into a relationship does not cause the child record to become dirty.

Because the transaction does not know which side of a relationship the adapter will want to save to the server, it considers the child, the old parent and the new parent as "dirty" immediately. This means that the application may not move any of the three records into a new transaction until the application commits or rolls back the current transaction.

Therefore, all three records involved in a relationship change must be in the same transaction (or the default transaction). If one of the records is clean and not in a transaction, the transaction will automatically adopt them. Otherwise (if the records are in different transactions, or if some of the records are dirty and not in a transaction), the relationship change will raise an exception.

Changes to the Transaction API

At present, a record's dirtiness (isDirty flag) corresponds 1:1 to its presence in one of its transaction's dirty buckets.

After this change, a transaction will move a record into a dirty bucket if it is involved in a relationship change as the old parent, new parent or child. In these cases, the record may not be isDirty.

Changes to the Adapter API

At present, when an application commits a transaction, it sends a list of its dirty changes (created, updated, and deleted) to the adapter.

If a record's parent does not have an id, the transaction will automatically hold off on sending it to the adapter's commit method. However, this makes it impossible for an adapter to implement non-relational semantics.

After this change, the transaction will send a list of all of its dirty records to the transaction. It will also send a list of all dirty relationships. The adapter is now responsible for determining whether it should actually persist a dirty record.

Let's take a look at an example of a relational adapter (foreign key semantics):

  1. postA has comments: [ commentA, commentB ]
  2. postB has comments: [ ]
  3. postA, postB, commentA and commentB are all clean (saved to the server)
  4. The application moves commentA from postA to postB
  5. When committed, the transaction sends the following to the adapter:
  • dirty: [ postA, postB, commentA ]
  • relationships: [{ child: commentA, oldParent: postA, newParent: postB }]
  1. The adapter notices that postA and postB do not have dirty attributes and do not need to be persisted in order to persist the changed relationship
  2. The adapter immediately calls store.didUpdateRecords([ postA, postB ])
  3. The store moves postA and postB out of the current transaction and back into the default transaction
  4. The adapter continues to persist commentA as usual. In order to get the foreign keys on the saved record, the adapter can call commentA.toJSON({ includeForeignKeys: true, namingConvention: this.namingConvention })

Note that if a record cannot be saved because it is waiting for a parent record to get its ID from the server (or vice versa in document-oriented adapters), it is now responsible for managing that flow. The DS.Adapter superclass will provide conveniences to make this easy.

Changes to Adapter Conveniences

The most important change to adapter conveniences is the addition of a new hook called shouldCommit.

The default commit method in DS.Adapter will perform the following steps for each record in the updated bucket:

  1. Create a new array
  2. If the record has dirty attributes, add it to the array
  3. If the records does not have dirty attributes, that means that it is either the child, old parent or new parent of a dirty relationship
    1. Call shouldCommit(record, relationships)
    2. If shouldCommit returns true, add it to the array
    3. Otherwise, immediately call store.didUpdateRecord(record)

This allows adapters to filter the updated bucket so it only contains records that it actually needs to save.

A shouldCommit hook for a relational store looks like:

If the record is the child of one of the relationships, return true

A shouldCommit hook for a document store looks like:

If the record is the old or new parent of one of the relationships, return true

TODO: describe shouldSave hook

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment