graphiti 2.0.0.beta.2 → 2.0.0.beta.4
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/.github/workflows/ci.yml +30 -86
- data/.github/workflows/docs.yml +60 -0
- data/.github/workflows/release.yml +8 -8
- data/.gitignore +7 -0
- data/.npmrc +9 -0
- data/.standard.yml +4 -1
- data/Appraisals +33 -32
- data/CHANGELOG.md +41 -0
- data/README.md +13 -2
- data/UPGRADING.md +2 -68
- data/docs/concepts/backends-and-models.md +122 -0
- data/docs/concepts/endpoints.md +183 -0
- data/docs/concepts/links.md +212 -0
- data/docs/concepts/overview.md +80 -0
- data/docs/concepts/persisting.md +376 -0
- data/docs/concepts/relationships.md +527 -0
- data/docs/concepts/resources.md +677 -0
- data/docs/getting-started/first-api.md +289 -0
- data/docs/getting-started/installation.md +185 -0
- data/docs/intro.md +307 -0
- data/docs/js/authentication.md +63 -0
- data/docs/js/ddau.md +20 -0
- data/docs/js/extra-params.md +41 -0
- data/docs/js/index.md +112 -0
- data/docs/js/installation.md +120 -0
- data/docs/js/middleware.md +72 -0
- data/docs/js/models.md +202 -0
- data/docs/js/reads.md +494 -0
- data/docs/js/state-syncing.md +100 -0
- data/docs/js/writes.md +373 -0
- data/docs/reference/vandal.md +63 -0
- data/docs/reference/why.md +13 -0
- data/docs/topics/authorization.md +155 -0
- data/docs/topics/caching.md +55 -0
- data/docs/topics/customizing-sideloads.md +156 -0
- data/docs/topics/debugging.md +216 -0
- data/docs/topics/error-handling.md +210 -0
- data/docs/topics/etags.md +46 -0
- data/docs/topics/hopping-relationships.md +149 -0
- data/docs/topics/json-attributes.md +77 -0
- data/docs/topics/openstruct-models.md +50 -0
- data/docs/topics/remote-resources.md +291 -0
- data/docs/topics/testing.md +894 -0
- data/docs/topics/without-activerecord.md +324 -0
- data/docs/tutorial/index.md +58 -0
- data/docs/tutorial/step_0.md +107 -0
- data/docs/tutorial/step_1.md +199 -0
- data/docs/tutorial/step_2.md +312 -0
- data/docs/tutorial/step_3.md +142 -0
- data/docs/tutorial/step_4.md +135 -0
- data/docs/tutorial/step_5.md +69 -0
- data/docs/tutorial/step_6.md +82 -0
- data/docs/tutorial/step_7.md +205 -0
- data/docs/tutorial/step_8.md +128 -0
- data/docs/tutorial/step_9.md +171 -0
- data/docs/upgrading.md +265 -0
- data/gemfiles/rails_7_1.gemfile +4 -3
- data/gemfiles/{rails_7_2_graphiti_rails.gemfile → rails_7_2.gemfile} +3 -3
- data/gemfiles/{rails_8_1_graphiti_rails.gemfile → rails_8_0.gemfile} +3 -3
- data/gemfiles/{rails_8_0_graphiti_rails.gemfile → rails_8_1.gemfile} +3 -3
- data/graphiti.gemspec +7 -5
- data/{deprecated_generators → lib/generators}/graphiti/api_test_generator.rb +7 -1
- data/{deprecated_generators → lib/generators}/graphiti/generator_mixin.rb +14 -1
- data/{deprecated_generators → lib/generators}/graphiti/install_generator.rb +19 -13
- data/{deprecated_generators → lib/generators}/graphiti/resource_generator.rb +43 -6
- data/{deprecated_generators → lib/generators}/graphiti/templates/index_request_spec.rb.erb +1 -1
- data/{deprecated_generators → lib/generators}/graphiti/templates/resource_reads_spec.rb.erb +6 -6
- data/{deprecated_generators → lib/generators}/graphiti/templates/show_request_spec.rb.erb +1 -1
- data/lib/graphiti/configuration.rb +2 -2
- data/lib/graphiti/error_serializers/conflict_request.rb +19 -0
- data/lib/graphiti/error_serializers/deprecated_constants.rb +48 -0
- data/lib/graphiti/error_serializers/invalid_request.rb +56 -0
- data/lib/graphiti/error_serializers/validation.rb +143 -0
- data/lib/graphiti/errors.rb +4 -23
- data/lib/graphiti/query.rb +1 -1
- data/lib/graphiti/rails/context.rb +33 -0
- data/lib/graphiti/rails/controller.rb +41 -0
- data/lib/graphiti/rails/debugging.rb +18 -0
- data/lib/graphiti/rails/exception_handlers.rb +77 -0
- data/lib/graphiti/rails/railtie.rb +139 -0
- data/lib/graphiti/rails/responders.rb +21 -0
- data/lib/graphiti/rails/test_helpers.rb +22 -0
- data/lib/graphiti/rails.rb +47 -29
- data/lib/graphiti/resource/configuration.rb +1 -0
- data/lib/graphiti/resource/interface.rb +2 -2
- data/lib/graphiti/resource/persistence.rb +14 -2
- data/lib/graphiti/resource/remote.rb +2 -2
- data/lib/graphiti/resource/sideloading.rb +1 -1
- data/lib/graphiti/resource.rb +13 -1
- data/lib/graphiti/responders.rb +7 -20
- data/lib/graphiti/schema.rb +5 -1
- data/lib/graphiti/schema_diff.rb +4 -0
- data/lib/graphiti/scope.rb +45 -37
- data/lib/graphiti/serializer.rb +6 -0
- data/lib/graphiti/sideload/belongs_to.rb +38 -5
- data/lib/graphiti/sideload/polymorphic_belongs_to.rb +27 -23
- data/lib/graphiti/sideload.rb +54 -35
- data/lib/graphiti/spec_helpers/errors.rb +73 -0
- data/lib/graphiti/spec_helpers/errors_proxy.rb +75 -0
- data/lib/graphiti/spec_helpers/helpers.rb +107 -0
- data/lib/graphiti/spec_helpers/node.rb +88 -0
- data/lib/graphiti/spec_helpers/rspec.rb +147 -0
- data/lib/graphiti/spec_helpers.rb +53 -0
- data/lib/graphiti/util/include_params.rb +2 -2
- data/lib/graphiti/util/persistence.rb +10 -11
- data/lib/graphiti/util/serializer_relationships.rb +41 -5
- data/lib/graphiti/version.rb +1 -1
- data/lib/graphiti-rails.rb +11 -0
- data/lib/graphiti.rb +34 -10
- data/lib/graphiti_errors.rb +11 -0
- data/lib/graphiti_spec_helpers/rspec.rb +3 -0
- data/lib/graphiti_spec_helpers.rb +11 -0
- data/lib/{graphiti/deprecated_tasks.rb → tasks/graphiti.rake} +6 -1
- data/package-lock.json +6199 -0
- data/package.json +5 -4
- data/website/.gitignore +20 -0
- data/website/README.md +43 -0
- data/website/docusaurus.config.js +141 -0
- data/website/package-lock.json +19474 -0
- data/website/package.json +46 -0
- data/website/sidebars.js +82 -0
- data/website/src/css/custom.css +58 -0
- data/website/src/pages/markdown-page.mdx +7 -0
- data/website/static/.nojekyll +0 -0
- data/website/static/1.13/2019/03/31/graphiti-1-0.html +205 -0
- data/website/static/1.13/2019/05/08/graphiti-1-1.html +212 -0
- data/website/static/1.13/2019/05/20/graphiti-1-2.html +214 -0
- data/website/static/1.13/2019/10/14/tutorial.html +198 -0
- data/website/static/1.13/CNAME +1 -0
- data/website/static/1.13/README.md +16 -0
- data/website/static/1.13/assets/css/syntax.css +60 -0
- data/website/static/1.13/assets/favicons/android-chrome-192x192.png +0 -0
- data/website/static/1.13/assets/favicons/android-chrome-256x256.png +0 -0
- data/website/static/1.13/assets/favicons/apple-touch-icon.png +0 -0
- data/website/static/1.13/assets/favicons/browserconfig.xml +9 -0
- data/website/static/1.13/assets/favicons/favicon-16x16.png +0 -0
- data/website/static/1.13/assets/favicons/favicon-32x32.png +0 -0
- data/website/static/1.13/assets/favicons/favicon.ico +0 -0
- data/website/static/1.13/assets/favicons/mstile-150x150.png +0 -0
- data/website/static/1.13/assets/favicons/safari-pinned-tab.svg +1 -0
- data/website/static/1.13/assets/favicons/site.webmanifest +19 -0
- data/website/static/1.13/assets/img/backend.gif +0 -0
- data/website/static/1.13/assets/img/conformity.png +0 -0
- data/website/static/1.13/assets/img/error_payload.png +0 -0
- data/website/static/1.13/assets/img/gh.png +0 -0
- data/website/static/1.13/assets/img/lifecycle.gif +0 -0
- data/website/static/1.13/assets/img/logo-500.png +0 -0
- data/website/static/1.13/assets/img/logo.png +0 -0
- data/website/static/1.13/assets/img/love-graffiti.jpg +0 -0
- data/website/static/1.13/assets/img/meta_total_count.png +0 -0
- data/website/static/1.13/assets/img/persist.jpg +0 -0
- data/website/static/1.13/assets/img/resource.gif +0 -0
- data/website/static/1.13/assets/img/rest-graffiti.jpg +0 -0
- data/website/static/1.13/assets/img/rest1.gif +0 -0
- data/website/static/1.13/assets/img/rest2.gif +0 -0
- data/website/static/1.13/assets/img/rest3.gif +0 -0
- data/website/static/1.13/assets/img/rethink-rest-graffiti.jpg +0 -0
- data/website/static/1.13/assets/img/why.png +0 -0
- data/website/static/1.13/assets/js/highlight.pack.js +2 -0
- data/website/static/1.13/assets/main.css +15518 -0
- data/website/static/1.13/assets/main.css.map +1 -0
- data/website/static/1.13/bin/bundle +109 -0
- data/website/static/1.13/bin/jekyll +27 -0
- data/website/static/1.13/bin/kramdown +27 -0
- data/website/static/1.13/bin/listen +27 -0
- data/website/static/1.13/bin/rake +27 -0
- data/website/static/1.13/bin/rougify +27 -0
- data/website/static/1.13/bin/safe_yaml +27 -0
- data/website/static/1.13/bin/sass +27 -0
- data/website/static/1.13/bin/sass-convert +27 -0
- data/website/static/1.13/bin/scss +27 -0
- data/website/static/1.13/blog.html +259 -0
- data/website/static/1.13/cheatsheet.html +316 -0
- data/website/static/1.13/cookbooks/authorization.md +0 -0
- data/website/static/1.13/cookbooks/caching.md +0 -0
- data/website/static/1.13/cookbooks/customizing-sideloads.html +325 -0
- data/website/static/1.13/cookbooks/etags.md +0 -0
- data/website/static/1.13/cookbooks/hopping-relationships.html +324 -0
- data/website/static/1.13/cookbooks/json_attributes.md +0 -0
- data/website/static/1.13/cookbooks/openstruct-models.md +0 -0
- data/website/static/1.13/cookbooks/remote-resources.md +0 -0
- data/website/static/1.13/cookbooks/without-activerecord.html +510 -0
- data/website/static/1.13/features.html +249 -0
- data/website/static/1.13/feed.xml +106 -0
- data/website/static/1.13/guides/concepts/backends-and-models.html +467 -0
- data/website/static/1.13/guides/concepts/debugging.html +440 -0
- data/website/static/1.13/guides/concepts/endpoints.html +432 -0
- data/website/static/1.13/guides/concepts/error-handling.html +396 -0
- data/website/static/1.13/guides/concepts/links.html +501 -0
- data/website/static/1.13/guides/concepts/remote-resources.html +536 -0
- data/website/static/1.13/guides/concepts/resources.html +2176 -0
- data/website/static/1.13/guides/concepts/testing.html +1469 -0
- data/website/static/1.13/guides/getting-started/installation.html +420 -0
- data/website/static/1.13/guides/graphiti-rails-migration.html +242 -0
- data/website/static/1.13/guides/index.html +269 -0
- data/website/static/1.13/guides/overview.html +325 -0
- data/website/static/1.13/guides/upgrading-2-0.html +193 -0
- data/website/static/1.13/guides/upgrading.html +314 -0
- data/website/static/1.13/guides/vandal.html +282 -0
- data/website/static/1.13/guides/why.html +1121 -0
- data/website/static/1.13/index.html +72 -0
- data/website/static/1.13/js/authentication.html +295 -0
- data/website/static/1.13/js/ddau.html +238 -0
- data/website/static/1.13/js/extra-params.html +270 -0
- data/website/static/1.13/js/index.html +321 -0
- data/website/static/1.13/js/installation.html +637 -0
- data/website/static/1.13/js/introduction.html +257 -0
- data/website/static/1.13/js/middleware.html +318 -0
- data/website/static/1.13/js/reads/fieldsets.html +271 -0
- data/website/static/1.13/js/reads/filtering.html +289 -0
- data/website/static/1.13/js/reads/includes.html +260 -0
- data/website/static/1.13/js/reads/index.html +497 -0
- data/website/static/1.13/js/reads/nested-queries.html +353 -0
- data/website/static/1.13/js/reads/pagination.html +260 -0
- data/website/static/1.13/js/reads/sorting.html +265 -0
- data/website/static/1.13/js/reads/statistics.html +289 -0
- data/website/static/1.13/js/state-syncing.html +340 -0
- data/website/static/1.13/js/writes/deferred.html +296 -0
- data/website/static/1.13/js/writes/dirty-tracking.html +399 -0
- data/website/static/1.13/js/writes/index.html +391 -0
- data/website/static/1.13/js/writes/nested.html +330 -0
- data/website/static/1.13/js/writes/validations.html +272 -0
- data/website/static/1.13/quickstart.html +660 -0
- data/website/static/1.13/template +161 -0
- data/website/static/1.13/tutorial/index.html +250 -0
- data/website/static/1.13/tutorial/step_0.html +292 -0
- data/website/static/1.13/tutorial/step_1.html +517 -0
- data/website/static/1.13/tutorial/step_2.html +481 -0
- data/website/static/1.13/tutorial/step_3.html +323 -0
- data/website/static/1.13/tutorial/step_4.html +318 -0
- data/website/static/1.13/tutorial/step_5.html +265 -0
- data/website/static/1.13/tutorial/step_6.html +276 -0
- data/website/static/1.13/tutorial/step_7.html +390 -0
- data/website/static/1.13/tutorial/step_8.html +316 -0
- data/website/static/1.13/tutorial/step_9.html +365 -0
- data/website/static/assets/img/error_payload.png +0 -0
- data/website/static/assets/img/legacy/legacy-0378a3bb39.png +0 -0
- data/website/static/assets/img/legacy/legacy-05bbd3e5fd.png +0 -0
- data/website/static/assets/img/legacy/legacy-07aa104495.png +0 -0
- data/website/static/assets/img/legacy/legacy-0c75a16b3a.gif +0 -0
- data/website/static/assets/img/legacy/legacy-3076df6209.png +0 -0
- data/website/static/assets/img/legacy/legacy-7f6889bc89.png +0 -0
- data/website/static/assets/img/legacy/legacy-a2cc4363c3.png +0 -0
- data/website/static/assets/img/legacy/legacy-f67cfa89ab.png +0 -0
- data/website/static/assets/img/meta_total_count.png +0 -0
- data/website/static/img/docusaurus-social-card.jpg +0 -0
- data/website/static/img/docusaurus.png +0 -0
- data/website/static/img/favicon.ico +0 -0
- data/website/static/img/logo.png +0 -0
- data/website/static/img/logo.svg +1 -0
- data/website/static/img/undraw_docusaurus_mountain.svg +171 -0
- data/website/static/img/undraw_docusaurus_react.svg +170 -0
- data/website/static/img/undraw_docusaurus_tree.svg +40 -0
- metadata +245 -46
- data/gemfiles/rails_6.gemfile +0 -18
- data/gemfiles/rails_6_graphiti_rails.gemfile +0 -19
- data/gemfiles/rails_7.gemfile +0 -18
- data/gemfiles/rails_7_1_graphiti_rails.gemfile +0 -19
- data/gemfiles/rails_7_graphiti_rails.gemfile +0 -19
- data/lib/graphiti/railtie.rb +0 -121
- /data/{deprecated_generators → lib/generators}/graphiti/resource_test_generator.rb +0 -0
- /data/{deprecated_generators → lib/generators}/graphiti/templates/application_resource.rb.erb +0 -0
- /data/{deprecated_generators → lib/generators}/graphiti/templates/controller.rb.erb +0 -0
- /data/{deprecated_generators → lib/generators}/graphiti/templates/create_request_spec.rb.erb +0 -0
- /data/{deprecated_generators → lib/generators}/graphiti/templates/destroy_request_spec.rb.erb +0 -0
- /data/{deprecated_generators → lib/generators}/graphiti/templates/resource.rb.erb +0 -0
- /data/{deprecated_generators → lib/generators}/graphiti/templates/resource_writes_spec.rb.erb +0 -0
- /data/{deprecated_generators → lib/generators}/graphiti/templates/update_request_spec.rb.erb +0 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: 'Middleware'
|
|
3
|
+
sidebar_position: 6
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
### Middleware
|
|
7
|
+
|
|
8
|
+
Middleware is helpful whenever you want to globally intercept request.
|
|
9
|
+
This is accomplished by assigning a `MiddlewareStack` to your `ApplicationRecord`. Each stack has `beforeFilters` and `afterFilters` where you can globally modify requests. If you `throw("abort")`, the
|
|
10
|
+
promise will be rejected.
|
|
11
|
+
|
|
12
|
+
Example: redirecting to the login page every time the server returns `401`:
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
import { MiddlewareStack } from 'spraypaint'
|
|
16
|
+
|
|
17
|
+
let middleware = new MiddlewareStack()
|
|
18
|
+
middleware.afterFilters.push((response, json) => {
|
|
19
|
+
if (response.status === 401) {
|
|
20
|
+
window.location.href = "/login"
|
|
21
|
+
throw("abort")
|
|
22
|
+
}
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
ApplicationRecord.middlewareStack = middleware
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
```javascript
|
|
29
|
+
var MiddlewareStack = spraypaint.MiddlewareStack;
|
|
30
|
+
|
|
31
|
+
var middleware = new MiddlewareStack();
|
|
32
|
+
middleware.afterFilters.push(function(response, json) {
|
|
33
|
+
if (response.status === 401) {
|
|
34
|
+
window.location.href = "/login";
|
|
35
|
+
throw("abort");
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
ApplicationRecord.middlewareStack = middleware;
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Example: adding a custom header before the request is sent:
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
import { MiddlewareStack } from 'spraypaint'
|
|
46
|
+
|
|
47
|
+
let middleware = new MiddlewareStack()
|
|
48
|
+
middleware.beforeFilters.push((url, options) => {
|
|
49
|
+
options.headers["CUSTOM-HEADER"] = "whatever"
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
ApplicationRecord.middlewareStack = middleware
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
```javascript
|
|
56
|
+
var MiddlewareStack = spraypaint.MiddlewareStack;
|
|
57
|
+
|
|
58
|
+
var middleware = new MiddlewareStack();
|
|
59
|
+
middleware.beforeFilters.push(function(url, options) {
|
|
60
|
+
options.headers["CUSTOM-HEADER"] = "whatever";
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
ApplicationRecord.middlewareStack = middleware;
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
<h2 id="next">
|
|
67
|
+
<a href="/js/authentication">
|
|
68
|
+
NEXT:
|
|
69
|
+
<small>Authentication</small>
|
|
70
|
+
»
|
|
71
|
+
</a>
|
|
72
|
+
</h2>
|
data/docs/js/models.md
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: 'Models'
|
|
3
|
+
sidebar_position: 3
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
### Defining Models
|
|
7
|
+
|
|
8
|
+
Once your `ApplicationRecord` base class is [connected to the API](/js/installation#connecting-to-the-api), define a model by giving it a `jsonapiType`:
|
|
9
|
+
|
|
10
|
+
```typescript
|
|
11
|
+
@Model()
|
|
12
|
+
class Person extends ApplicationRecord {
|
|
13
|
+
static jsonapiType = "people"
|
|
14
|
+
}
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
```javascript
|
|
18
|
+
const Person = ApplicationRecord.extend({
|
|
19
|
+
static: {
|
|
20
|
+
jsonapiType: "people"
|
|
21
|
+
}
|
|
22
|
+
})
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
With the above configuration, all `Person` endpoints will begin `http://my-api.com/api/v1/people`.
|
|
26
|
+
|
|
27
|
+
### Defining Attributes
|
|
28
|
+
|
|
29
|
+
`ActiveRecord` automatically sets attributes by introspecting database columns. We could do the same - `swagger.json` is our schema - but tend to agree with those who feel this aspect of `ActiveRecord` is a bit too "magical". In addition, explicitly defining our attributes can be used to track which applications are using which attributes of the API.
|
|
30
|
+
|
|
31
|
+
Though this is configurable, by default we expect the API to be `under_scored` and attributes to be `camelCased`.
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
@Model()
|
|
35
|
+
class Person extends ApplicationRecord {
|
|
36
|
+
// ... code ...
|
|
37
|
+
@Attr() firstName: string
|
|
38
|
+
@Attr() lastName: string
|
|
39
|
+
@Attr() age: number
|
|
40
|
+
|
|
41
|
+
get fullName() : string {
|
|
42
|
+
return `${this.firstName} ${this.lastName}`
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
let person = new Person({ firstName: "John" })
|
|
47
|
+
person.firstName // "John"
|
|
48
|
+
person.lastName = "Doe"
|
|
49
|
+
person.attributes // { firstName: "John", lastName: "Doe" }
|
|
50
|
+
person.fullName // "John Doe"
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
```javascript
|
|
54
|
+
const attr = spraypaint.attr
|
|
55
|
+
const Person = ApplicationRecord.extend({
|
|
56
|
+
// ... code ...
|
|
57
|
+
attrs: {
|
|
58
|
+
firstName: attr(),
|
|
59
|
+
lastName: attr(),
|
|
60
|
+
age: attr()
|
|
61
|
+
},
|
|
62
|
+
methods: {
|
|
63
|
+
fullName: function() {
|
|
64
|
+
return this.firstName + " " + this.lastName;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
var person = new Person({ firstName: "John" })
|
|
70
|
+
person.firstName // "John"
|
|
71
|
+
person.lastName = "Doe"
|
|
72
|
+
person.attributes // { firstName: "John", lastName: "Doe" }
|
|
73
|
+
person.fullName() // "John Doe"
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Attributes can be marked read-only, so they are never sent to the server on a write request:
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
@Attr({ persist: false }) createdAt: string
|
|
80
|
+
@Attr({ persist: false }) updatedAt: string
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
```javascript
|
|
84
|
+
attrs: {
|
|
85
|
+
createdAt: attr({ persist: false }),
|
|
86
|
+
updatedAt: attr({ persist: false })
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Defining Relationships
|
|
91
|
+
|
|
92
|
+
Just like `ActiveRecord`, there are `HasMany`, `BelongsTo`, and `HasOne` relationships:
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
@Model()
|
|
96
|
+
class Dog extends ApplicationRecord {
|
|
97
|
+
// ... code ...
|
|
98
|
+
@BelongsTo() person: Person[]
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
class Person extends ApplicationRecord {
|
|
102
|
+
// ... code ...
|
|
103
|
+
@HasMany() dogs: Dog[]
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
```javascript
|
|
108
|
+
const hasMany = spraypaint.hasMany
|
|
109
|
+
const belongsTo = spraypaint.belongsTo
|
|
110
|
+
|
|
111
|
+
const Person = ApplicationRecord.extend({
|
|
112
|
+
// ... code ...
|
|
113
|
+
attrs: {
|
|
114
|
+
dogs: hasMany()
|
|
115
|
+
}
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
const Dog = ApplicationRecord.extend({
|
|
119
|
+
// ... code ...
|
|
120
|
+
attrs: {
|
|
121
|
+
person: belongsTo()
|
|
122
|
+
}
|
|
123
|
+
})
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
By default, we expect the relationship name to correspond to a pluralized `jsonapiType` on a separate `Model`. If your models don't use this convention, feel free to supply it explicitly:
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
@Model()
|
|
130
|
+
class Dog extends ApplicationRecord {
|
|
131
|
+
// ... code ...
|
|
132
|
+
@BelongsTo('people') owner: Person[]
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// alternatively, specify the class directly
|
|
136
|
+
|
|
137
|
+
class Dog extends ApplicationRecord {
|
|
138
|
+
// ... code ...
|
|
139
|
+
@BelongsTo(Person) owner: Person[]
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
```javascript
|
|
144
|
+
const Dog = ApplicationRecord.extend({
|
|
145
|
+
// ... code ...
|
|
146
|
+
attrs: {
|
|
147
|
+
owner: belongsTo('people')
|
|
148
|
+
}
|
|
149
|
+
})
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Relationships can be:
|
|
153
|
+
|
|
154
|
+
* Assigned via constructor
|
|
155
|
+
* Assigned directly
|
|
156
|
+
* Automatically loaded via `.includes()` (see [reads](/js/reads))
|
|
157
|
+
* Saved in a single request `.save({ with: 'dogs' })` (see
|
|
158
|
+
[writes](/js/writes))
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
let dog = new Dog({ name: "Fido" })
|
|
162
|
+
let person = new Person({ dogs: [dog] })
|
|
163
|
+
person.dogs[0].name // "Fido"
|
|
164
|
+
|
|
165
|
+
let person = new Person()
|
|
166
|
+
person.dogs = [dog]
|
|
167
|
+
person.dogs[0].name // "Fido"
|
|
168
|
+
|
|
169
|
+
// Will auto-create Dog instance
|
|
170
|
+
let person = new Person({ dogs: [{ name: "Scooby" }] })
|
|
171
|
+
person.dogs[0].name // "Scooby"
|
|
172
|
+
|
|
173
|
+
let person = (await Person.includes('dogs')).data
|
|
174
|
+
person.dogs // array of Dog instances from the server
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
```javascript
|
|
178
|
+
var dog = new Dog({ name: "Fido" })
|
|
179
|
+
var person = new Person({ dogs: [dog] })
|
|
180
|
+
person.dogs[0].name // "Fido"
|
|
181
|
+
|
|
182
|
+
let person = new Person()
|
|
183
|
+
person.dogs = [dog]
|
|
184
|
+
person.dogs[0].name // "Fido"
|
|
185
|
+
|
|
186
|
+
// Will auto-create Dog instance
|
|
187
|
+
var person = new Person({ dogs: [{ name: "Scooby" }] })
|
|
188
|
+
person.dogs[0].name // "Scooby"
|
|
189
|
+
|
|
190
|
+
Person.includes('dogs').then((response) => {
|
|
191
|
+
var person = response.data
|
|
192
|
+
person.dogs // array of Dog instances from the server
|
|
193
|
+
})
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
<h2 id="next">
|
|
197
|
+
<a href="/js/reads">
|
|
198
|
+
NEXT:
|
|
199
|
+
<small>Reads</small>
|
|
200
|
+
»
|
|
201
|
+
</a>
|
|
202
|
+
</h2>
|