@rails/activestorage 7.0.0-alpha2 → 7.0.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.
|
@@ -508,7 +508,7 @@ function toArray(value) {
|
|
|
508
508
|
}
|
|
509
509
|
|
|
510
510
|
class BlobRecord {
|
|
511
|
-
constructor(file, checksum, url) {
|
|
511
|
+
constructor(file, checksum, url, directUploadToken, attachmentName) {
|
|
512
512
|
this.file = file;
|
|
513
513
|
this.attributes = {
|
|
514
514
|
filename: file.name,
|
|
@@ -516,6 +516,8 @@ class BlobRecord {
|
|
|
516
516
|
byte_size: file.size,
|
|
517
517
|
checksum: checksum
|
|
518
518
|
};
|
|
519
|
+
this.directUploadToken = directUploadToken;
|
|
520
|
+
this.attachmentName = attachmentName;
|
|
519
521
|
this.xhr = new XMLHttpRequest;
|
|
520
522
|
this.xhr.open("POST", url, true);
|
|
521
523
|
this.xhr.responseType = "json";
|
|
@@ -543,7 +545,9 @@ class BlobRecord {
|
|
|
543
545
|
create(callback) {
|
|
544
546
|
this.callback = callback;
|
|
545
547
|
this.xhr.send(JSON.stringify({
|
|
546
|
-
blob: this.attributes
|
|
548
|
+
blob: this.attributes,
|
|
549
|
+
direct_upload_token: this.directUploadToken,
|
|
550
|
+
attachment_name: this.attachmentName
|
|
547
551
|
}));
|
|
548
552
|
}
|
|
549
553
|
requestDidLoad(event) {
|
|
@@ -604,10 +608,12 @@ class BlobUpload {
|
|
|
604
608
|
let id = 0;
|
|
605
609
|
|
|
606
610
|
class DirectUpload {
|
|
607
|
-
constructor(file, url, delegate) {
|
|
611
|
+
constructor(file, url, serviceName, attachmentName, delegate) {
|
|
608
612
|
this.id = ++id;
|
|
609
613
|
this.file = file;
|
|
610
614
|
this.url = url;
|
|
615
|
+
this.serviceName = serviceName;
|
|
616
|
+
this.attachmentName = attachmentName;
|
|
611
617
|
this.delegate = delegate;
|
|
612
618
|
}
|
|
613
619
|
create(callback) {
|
|
@@ -616,7 +622,7 @@ class DirectUpload {
|
|
|
616
622
|
callback(error);
|
|
617
623
|
return;
|
|
618
624
|
}
|
|
619
|
-
const blob = new BlobRecord(this.file, checksum, this.url);
|
|
625
|
+
const blob = new BlobRecord(this.file, checksum, this.url, this.serviceName, this.attachmentName);
|
|
620
626
|
notify(this.delegate, "directUploadWillCreateBlobWithXHR", blob.xhr);
|
|
621
627
|
blob.create((error => {
|
|
622
628
|
if (error) {
|
|
@@ -647,7 +653,7 @@ class DirectUploadController {
|
|
|
647
653
|
constructor(input, file) {
|
|
648
654
|
this.input = input;
|
|
649
655
|
this.file = file;
|
|
650
|
-
this.directUpload = new DirectUpload(this.file, this.url, this);
|
|
656
|
+
this.directUpload = new DirectUpload(this.file, this.url, this.directUploadToken, this.attachmentName, this);
|
|
651
657
|
this.dispatch("initialize");
|
|
652
658
|
}
|
|
653
659
|
start(callback) {
|
|
@@ -678,6 +684,12 @@ class DirectUploadController {
|
|
|
678
684
|
get url() {
|
|
679
685
|
return this.input.getAttribute("data-direct-upload-url");
|
|
680
686
|
}
|
|
687
|
+
get directUploadToken() {
|
|
688
|
+
return this.input.getAttribute("data-direct-upload-token");
|
|
689
|
+
}
|
|
690
|
+
get attachmentName() {
|
|
691
|
+
return this.input.getAttribute("data-direct-upload-attachment-name");
|
|
692
|
+
}
|
|
681
693
|
dispatch(name, detail = {}) {
|
|
682
694
|
detail.file = this.file;
|
|
683
695
|
detail.id = this.directUpload.id;
|
|
@@ -503,7 +503,7 @@
|
|
|
503
503
|
}
|
|
504
504
|
}
|
|
505
505
|
class BlobRecord {
|
|
506
|
-
constructor(file, checksum, url) {
|
|
506
|
+
constructor(file, checksum, url, directUploadToken, attachmentName) {
|
|
507
507
|
this.file = file;
|
|
508
508
|
this.attributes = {
|
|
509
509
|
filename: file.name,
|
|
@@ -511,6 +511,8 @@
|
|
|
511
511
|
byte_size: file.size,
|
|
512
512
|
checksum: checksum
|
|
513
513
|
};
|
|
514
|
+
this.directUploadToken = directUploadToken;
|
|
515
|
+
this.attachmentName = attachmentName;
|
|
514
516
|
this.xhr = new XMLHttpRequest;
|
|
515
517
|
this.xhr.open("POST", url, true);
|
|
516
518
|
this.xhr.responseType = "json";
|
|
@@ -538,7 +540,9 @@
|
|
|
538
540
|
create(callback) {
|
|
539
541
|
this.callback = callback;
|
|
540
542
|
this.xhr.send(JSON.stringify({
|
|
541
|
-
blob: this.attributes
|
|
543
|
+
blob: this.attributes,
|
|
544
|
+
direct_upload_token: this.directUploadToken,
|
|
545
|
+
attachment_name: this.attachmentName
|
|
542
546
|
}));
|
|
543
547
|
}
|
|
544
548
|
requestDidLoad(event) {
|
|
@@ -596,10 +600,12 @@
|
|
|
596
600
|
}
|
|
597
601
|
let id = 0;
|
|
598
602
|
class DirectUpload {
|
|
599
|
-
constructor(file, url, delegate) {
|
|
603
|
+
constructor(file, url, serviceName, attachmentName, delegate) {
|
|
600
604
|
this.id = ++id;
|
|
601
605
|
this.file = file;
|
|
602
606
|
this.url = url;
|
|
607
|
+
this.serviceName = serviceName;
|
|
608
|
+
this.attachmentName = attachmentName;
|
|
603
609
|
this.delegate = delegate;
|
|
604
610
|
}
|
|
605
611
|
create(callback) {
|
|
@@ -608,7 +614,7 @@
|
|
|
608
614
|
callback(error);
|
|
609
615
|
return;
|
|
610
616
|
}
|
|
611
|
-
const blob = new BlobRecord(this.file, checksum, this.url);
|
|
617
|
+
const blob = new BlobRecord(this.file, checksum, this.url, this.serviceName, this.attachmentName);
|
|
612
618
|
notify(this.delegate, "directUploadWillCreateBlobWithXHR", blob.xhr);
|
|
613
619
|
blob.create((error => {
|
|
614
620
|
if (error) {
|
|
@@ -637,7 +643,7 @@
|
|
|
637
643
|
constructor(input, file) {
|
|
638
644
|
this.input = input;
|
|
639
645
|
this.file = file;
|
|
640
|
-
this.directUpload = new DirectUpload(this.file, this.url, this);
|
|
646
|
+
this.directUpload = new DirectUpload(this.file, this.url, this.directUploadToken, this.attachmentName, this);
|
|
641
647
|
this.dispatch("initialize");
|
|
642
648
|
}
|
|
643
649
|
start(callback) {
|
|
@@ -668,6 +674,12 @@
|
|
|
668
674
|
get url() {
|
|
669
675
|
return this.input.getAttribute("data-direct-upload-url");
|
|
670
676
|
}
|
|
677
|
+
get directUploadToken() {
|
|
678
|
+
return this.input.getAttribute("data-direct-upload-token");
|
|
679
|
+
}
|
|
680
|
+
get attachmentName() {
|
|
681
|
+
return this.input.getAttribute("data-direct-upload-attachment-name");
|
|
682
|
+
}
|
|
671
683
|
dispatch(name, detail = {}) {
|
|
672
684
|
detail.file = this.file;
|
|
673
685
|
detail.id = this.directUpload.id;
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rails/activestorage",
|
|
3
|
-
"version": "7.0.0
|
|
3
|
+
"version": "7.0.0",
|
|
4
4
|
"description": "Attach cloud and local files in Rails applications",
|
|
5
|
+
"module": "app/assets/javascripts/activestorage.esm.js",
|
|
5
6
|
"main": "app/assets/javascripts/activestorage.js",
|
|
6
|
-
"type": "module",
|
|
7
7
|
"files": [
|
|
8
8
|
"app/assets/javascripts/*.js",
|
|
9
9
|
"src/*.js"
|
package/src/blob_record.js
CHANGED
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
import { getMetaValue } from "./helpers"
|
|
2
2
|
|
|
3
3
|
export class BlobRecord {
|
|
4
|
-
constructor(file, checksum, url) {
|
|
4
|
+
constructor(file, checksum, url, directUploadToken, attachmentName) {
|
|
5
5
|
this.file = file
|
|
6
6
|
|
|
7
7
|
this.attributes = {
|
|
8
8
|
filename: file.name,
|
|
9
9
|
content_type: file.type || "application/octet-stream",
|
|
10
10
|
byte_size: file.size,
|
|
11
|
-
checksum: checksum
|
|
11
|
+
checksum: checksum,
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
this.directUploadToken = directUploadToken
|
|
15
|
+
this.attachmentName = attachmentName
|
|
16
|
+
|
|
14
17
|
this.xhr = new XMLHttpRequest
|
|
15
18
|
this.xhr.open("POST", url, true)
|
|
16
19
|
this.xhr.responseType = "json"
|
|
@@ -43,7 +46,11 @@ export class BlobRecord {
|
|
|
43
46
|
|
|
44
47
|
create(callback) {
|
|
45
48
|
this.callback = callback
|
|
46
|
-
this.xhr.send(JSON.stringify({
|
|
49
|
+
this.xhr.send(JSON.stringify({
|
|
50
|
+
blob: this.attributes,
|
|
51
|
+
direct_upload_token: this.directUploadToken,
|
|
52
|
+
attachment_name: this.attachmentName
|
|
53
|
+
}))
|
|
47
54
|
}
|
|
48
55
|
|
|
49
56
|
requestDidLoad(event) {
|
package/src/direct_upload.js
CHANGED
|
@@ -5,10 +5,12 @@ import { BlobUpload } from "./blob_upload"
|
|
|
5
5
|
let id = 0
|
|
6
6
|
|
|
7
7
|
export class DirectUpload {
|
|
8
|
-
constructor(file, url, delegate) {
|
|
8
|
+
constructor(file, url, serviceName, attachmentName, delegate) {
|
|
9
9
|
this.id = ++id
|
|
10
10
|
this.file = file
|
|
11
11
|
this.url = url
|
|
12
|
+
this.serviceName = serviceName
|
|
13
|
+
this.attachmentName = attachmentName
|
|
12
14
|
this.delegate = delegate
|
|
13
15
|
}
|
|
14
16
|
|
|
@@ -19,7 +21,7 @@ export class DirectUpload {
|
|
|
19
21
|
return
|
|
20
22
|
}
|
|
21
23
|
|
|
22
|
-
const blob = new BlobRecord(this.file, checksum, this.url)
|
|
24
|
+
const blob = new BlobRecord(this.file, checksum, this.url, this.serviceName, this.attachmentName)
|
|
23
25
|
notify(this.delegate, "directUploadWillCreateBlobWithXHR", blob.xhr)
|
|
24
26
|
|
|
25
27
|
blob.create(error => {
|
|
@@ -5,7 +5,7 @@ export class DirectUploadController {
|
|
|
5
5
|
constructor(input, file) {
|
|
6
6
|
this.input = input
|
|
7
7
|
this.file = file
|
|
8
|
-
this.directUpload = new DirectUpload(this.file, this.url, this)
|
|
8
|
+
this.directUpload = new DirectUpload(this.file, this.url, this.directUploadToken, this.attachmentName, this)
|
|
9
9
|
this.dispatch("initialize")
|
|
10
10
|
}
|
|
11
11
|
|
|
@@ -41,6 +41,14 @@ export class DirectUploadController {
|
|
|
41
41
|
return this.input.getAttribute("data-direct-upload-url")
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
get directUploadToken() {
|
|
45
|
+
return this.input.getAttribute("data-direct-upload-token")
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
get attachmentName() {
|
|
49
|
+
return this.input.getAttribute("data-direct-upload-attachment-name")
|
|
50
|
+
}
|
|
51
|
+
|
|
44
52
|
dispatch(name, detail = {}) {
|
|
45
53
|
detail.file = this.file
|
|
46
54
|
detail.id = this.directUpload.id
|
package/CHANGELOG.md
DELETED
|
@@ -1,226 +0,0 @@
|
|
|
1
|
-
## Rails 7.0.0.alpha2 (September 15, 2021) ##
|
|
2
|
-
|
|
3
|
-
* No changes.
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
## Rails 7.0.0.alpha1 (September 15, 2021) ##
|
|
7
|
-
|
|
8
|
-
* Emit Active Support instrumentation events from Active Storage analyzers.
|
|
9
|
-
|
|
10
|
-
Fixes #42930
|
|
11
|
-
|
|
12
|
-
*Shouichi Kamiya*
|
|
13
|
-
|
|
14
|
-
* Add support for byte range requests
|
|
15
|
-
|
|
16
|
-
*Tom Prats*
|
|
17
|
-
|
|
18
|
-
* Attachments can be deleted after their association is no longer defined.
|
|
19
|
-
|
|
20
|
-
Fixes #42514
|
|
21
|
-
|
|
22
|
-
*Don Sisco*
|
|
23
|
-
|
|
24
|
-
* Make `vips` the default variant processor for new apps.
|
|
25
|
-
|
|
26
|
-
See the upgrade guide for instructions on converting from `mini_magick` to `vips`. `mini_magick` is
|
|
27
|
-
not deprecated, existing apps can keep using it.
|
|
28
|
-
|
|
29
|
-
*Breno Gazzola*
|
|
30
|
-
|
|
31
|
-
* Deprecate `ActiveStorage::Current.host` in favor of `ActiveStorage::Current.url_options` which accepts
|
|
32
|
-
a host, protocol and port.
|
|
33
|
-
|
|
34
|
-
*Santiago Bartesaghi*
|
|
35
|
-
|
|
36
|
-
* Allow using [IAM](https://cloud.google.com/storage/docs/access-control/signed-urls) when signing URLs with GCS.
|
|
37
|
-
|
|
38
|
-
```yaml
|
|
39
|
-
gcs:
|
|
40
|
-
service: GCS
|
|
41
|
-
...
|
|
42
|
-
iam: true
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
*RRethy*
|
|
46
|
-
|
|
47
|
-
* OpenSSL constants are now used for Digest computations.
|
|
48
|
-
|
|
49
|
-
*Dirkjan Bussink*
|
|
50
|
-
|
|
51
|
-
* Deprecate `config.active_storage.replace_on_assign_to_many`. Future versions of Rails
|
|
52
|
-
will behave the same way as when the config is set to `true`.
|
|
53
|
-
|
|
54
|
-
*Santiago Bartesaghi*
|
|
55
|
-
|
|
56
|
-
* Remove deprecated methods: `build_after_upload`, `create_after_upload!` in favor of `create_and_upload!`,
|
|
57
|
-
and `service_url` in favor of `url`.
|
|
58
|
-
|
|
59
|
-
*Santiago Bartesaghi*
|
|
60
|
-
|
|
61
|
-
* Add support of `strict_loading_by_default` to `ActiveStorage::Representations` controllers.
|
|
62
|
-
|
|
63
|
-
*Anton Topchii*, *Andrew White*
|
|
64
|
-
|
|
65
|
-
* Allow to detach an attachment when record is not persisted.
|
|
66
|
-
|
|
67
|
-
*Jacopo Beschi*
|
|
68
|
-
|
|
69
|
-
* Use libvips instead of ImageMagick to analyze images when `active_storage.variant_processor = vips`.
|
|
70
|
-
|
|
71
|
-
*Breno Gazzola*
|
|
72
|
-
|
|
73
|
-
* Add metadata value for presence of video channel in video blobs.
|
|
74
|
-
|
|
75
|
-
The `metadata` attribute of video blobs has a new boolean key named `video` that is set to
|
|
76
|
-
`true` if the file has an video channel and `false` if it doesn't.
|
|
77
|
-
|
|
78
|
-
*Breno Gazzola*
|
|
79
|
-
|
|
80
|
-
* Deprecate usage of `purge` and `purge_later` from the association extension.
|
|
81
|
-
|
|
82
|
-
*Jacopo Beschi*
|
|
83
|
-
|
|
84
|
-
* Passing extra parameters in `ActiveStorage::Blob#url` to S3 Client.
|
|
85
|
-
|
|
86
|
-
This allows calls of `ActiveStorage::Blob#url` to have more interaction with
|
|
87
|
-
the S3 Presigner, enabling, amongst other options, custom S3 domain URL
|
|
88
|
-
Generation.
|
|
89
|
-
|
|
90
|
-
```ruby
|
|
91
|
-
blob = ActiveStorage::Blob.last
|
|
92
|
-
|
|
93
|
-
blob.url # => https://<bucket-name>.s3.<region>.amazonaws.com/<key>
|
|
94
|
-
blob.url(virtual_host: true) # => # => https://<bucket-name>/<key>
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
*josegomezr*
|
|
98
|
-
|
|
99
|
-
* Allow setting a `Cache-Control` on files uploaded to GCS.
|
|
100
|
-
|
|
101
|
-
```yaml
|
|
102
|
-
gcs:
|
|
103
|
-
service: GCS
|
|
104
|
-
...
|
|
105
|
-
cache_control: "public, max-age=3600"
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
*maleblond*
|
|
109
|
-
|
|
110
|
-
* The parameters sent to `ffmpeg` for generating a video preview image are now
|
|
111
|
-
configurable under `config.active_storage.video_preview_arguments`.
|
|
112
|
-
|
|
113
|
-
*Brendon Muir*
|
|
114
|
-
|
|
115
|
-
* The ActiveStorage video previewer will now use scene change detection to generate
|
|
116
|
-
better preview images (rather than the previous default of using the first frame
|
|
117
|
-
of the video). This change requires FFmpeg v3.4+.
|
|
118
|
-
|
|
119
|
-
*Jonathan Hefner*
|
|
120
|
-
|
|
121
|
-
* Add support for ActiveStorage expiring URLs.
|
|
122
|
-
|
|
123
|
-
```ruby
|
|
124
|
-
rails_blob_path(user.avatar, disposition: "attachment", expires_in: 30.minutes)
|
|
125
|
-
|
|
126
|
-
<%= image_tag rails_blob_path(user.avatar.variant(resize: "100x100"), expires_in: 30.minutes) %>
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
If you want to set default expiration time for ActiveStorage URLs throughout your application, set `config.active_storage.urls_expire_in`.
|
|
130
|
-
|
|
131
|
-
*aki77*
|
|
132
|
-
|
|
133
|
-
* Allow to purge an attachment when record is not persisted for `has_many_attached`.
|
|
134
|
-
|
|
135
|
-
*Jacopo Beschi*
|
|
136
|
-
|
|
137
|
-
* Add `with_all_variant_records` method to eager load all variant records on an attachment at once.
|
|
138
|
-
`with_attached_image` scope now eager loads variant records if using variant tracking.
|
|
139
|
-
|
|
140
|
-
*Alex Ghiculescu*
|
|
141
|
-
|
|
142
|
-
* Add metadata value for presence of audio channel in video blobs.
|
|
143
|
-
|
|
144
|
-
The `metadata` attribute of video blobs has a new boolean key named `audio` that is set to
|
|
145
|
-
`true` if the file has an audio channel and `false` if it doesn't.
|
|
146
|
-
|
|
147
|
-
*Breno Gazzola*
|
|
148
|
-
|
|
149
|
-
* Adds analyzer for audio files.
|
|
150
|
-
|
|
151
|
-
*Breno Gazzola*
|
|
152
|
-
|
|
153
|
-
* Respect Active Record's primary_key_type in Active Storage migrations.
|
|
154
|
-
|
|
155
|
-
*fatkodima*
|
|
156
|
-
|
|
157
|
-
* Allow `expires_in` for ActiveStorage signed ids.
|
|
158
|
-
|
|
159
|
-
*aki77*
|
|
160
|
-
|
|
161
|
-
* Allow to purge an attachment when record is not persisted for `has_one_attached`.
|
|
162
|
-
|
|
163
|
-
*Jacopo Beschi*
|
|
164
|
-
|
|
165
|
-
* Add a load hook called `active_storage_variant_record` (providing `ActiveStorage::VariantRecord`)
|
|
166
|
-
to allow for overriding aspects of the `ActiveStorage::VariantRecord` class. This makes
|
|
167
|
-
`ActiveStorage::VariantRecord` consistent with `ActiveStorage::Blob` and `ActiveStorage::Attachment`
|
|
168
|
-
that already have load hooks.
|
|
169
|
-
|
|
170
|
-
*Brendon Muir*
|
|
171
|
-
|
|
172
|
-
* `ActiveStorage::PreviewError` is raised when a previewer is unable to generate a preview image.
|
|
173
|
-
|
|
174
|
-
*Alex Robbin*
|
|
175
|
-
|
|
176
|
-
* Add `ActiveStorage::Streaming` module that can be included in a controller to get access to `#send_blob_stream`,
|
|
177
|
-
which wraps the new `ActionController::Base#send_stream` method to stream a blob from cloud storage:
|
|
178
|
-
|
|
179
|
-
```ruby
|
|
180
|
-
class MyPublicBlobsController < ApplicationController
|
|
181
|
-
include ActiveStorage::SetBlob, ActiveStorage::Streaming
|
|
182
|
-
|
|
183
|
-
def show
|
|
184
|
-
http_cache_forever(public: true) do
|
|
185
|
-
send_blob_stream @blob, disposition: params[:disposition]
|
|
186
|
-
end
|
|
187
|
-
end
|
|
188
|
-
end
|
|
189
|
-
```
|
|
190
|
-
|
|
191
|
-
*DHH*
|
|
192
|
-
|
|
193
|
-
* Add ability to use pre-defined variants.
|
|
194
|
-
|
|
195
|
-
```ruby
|
|
196
|
-
class User < ActiveRecord::Base
|
|
197
|
-
has_one_attached :avatar do |attachable|
|
|
198
|
-
attachable.variant :thumb, resize: "100x100"
|
|
199
|
-
attachable.variant :medium, resize: "300x300", monochrome: true
|
|
200
|
-
end
|
|
201
|
-
end
|
|
202
|
-
|
|
203
|
-
class Gallery < ActiveRecord::Base
|
|
204
|
-
has_many_attached :photos do |attachable|
|
|
205
|
-
attachable.variant :thumb, resize: "100x100"
|
|
206
|
-
attachable.variant :medium, resize: "300x300", monochrome: true
|
|
207
|
-
end
|
|
208
|
-
end
|
|
209
|
-
|
|
210
|
-
<%= image_tag user.avatar.variant(:thumb) %>
|
|
211
|
-
```
|
|
212
|
-
|
|
213
|
-
*fatkodima*
|
|
214
|
-
|
|
215
|
-
* After setting `config.active_storage.resolve_model_to_route = :rails_storage_proxy`
|
|
216
|
-
`rails_blob_path` and `rails_representation_path` will generate proxy URLs by default.
|
|
217
|
-
|
|
218
|
-
*Ali Ismayilov*
|
|
219
|
-
|
|
220
|
-
* Declare `ActiveStorage::FixtureSet` and `ActiveStorage::FixtureSet.blob` to
|
|
221
|
-
improve fixture integration.
|
|
222
|
-
|
|
223
|
-
*Sean Doyle*
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
Please check [6-1-stable](https://github.com/rails/rails/blob/6-1-stable/activestorage/CHANGELOG.md) for previous changes.
|