presigned_upload 0.1.2 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/README.md +160 -3
- data/lib/generators/presigned_upload/install_generator.rb +13 -0
- data/lib/generators/presigned_upload/templates/initializer.rb +13 -0
- data/lib/generators/presigned_upload/templates/migration.rb +0 -1
- data/lib/presigned_upload/configuration.rb +2 -2
- data/lib/presigned_upload/models.rb +15 -16
- data/lib/presigned_upload/uploadable.rb +15 -6
- data/lib/presigned_upload/version.rb +1 -1
- metadata +11 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ca293e74541ff82d393ac583e295699df5f5fbf80d32c2b4f4365fc820e76959
|
4
|
+
data.tar.gz: 75e337709fe872cf5f5fc71470017f37e5b6dc5266152ae166cec6d29fcc58bc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ee83ec4b1fd4a499330eeb44eeaf0d75c5ac2f413109ef3fe41e4f555d3c1616d23fe4c819eeb761fa399e757177c71cf382045a0d1db058ac49422d1a8456a4
|
7
|
+
data.tar.gz: 4d57cc6b0317400c8637e2cfc71bed2a3bc3bfdfb04be6ab82d0a33fd0963f6ea68799697498543e651f958864e7299f7e1d0b1c8c0e650303d95a541c11c32a
|
data/CHANGELOG.md
CHANGED
@@ -3,3 +3,11 @@
|
|
3
3
|
## [0.1.0] - 2023-11-23
|
4
4
|
|
5
5
|
- Initial release
|
6
|
+
|
7
|
+
## [0.2.0] - 2024-07-25
|
8
|
+
|
9
|
+
- Removed `store_path` attribute from migration
|
10
|
+
- Moved `store_path` from `before_create` callback to `store_dir` instance method
|
11
|
+
- The `store_path` now is mounted when it is called, joining `store_dir` + `original_name`
|
12
|
+
- Added README documentation and installation instructions
|
13
|
+
- Added `install_generator.rb` to generate the PresignedUpload default initializer
|
data/README.md
CHANGED
@@ -1,11 +1,13 @@
|
|
1
1
|
# PresignedUpload
|
2
2
|
|
3
|
-
|
3
|
+
Control file uploads via presigned URLs to cloud storage services.
|
4
4
|
|
5
5
|
A presigned URL is generated by a server that grants temporary and controlled access to a specific resource. In the context of file uploads, it allows clients to directly upload files to a designated storage location without the server being directly involved in the transfer process.
|
6
6
|
|
7
7
|
## Installation
|
8
8
|
|
9
|
+
### 1. Add the gem into your project
|
10
|
+
|
9
11
|
Add this to your Gemfile and run `bundle install`.
|
10
12
|
|
11
13
|
```ruby
|
@@ -17,13 +19,168 @@ Or install it yourself as:
|
|
17
19
|
gem install presigned_upload
|
18
20
|
```
|
19
21
|
|
22
|
+
### 2. Create the initializer file
|
23
|
+
|
24
|
+
Run the generator to create the initializer file:
|
25
|
+
```sh
|
26
|
+
rails g presigned_upload:install
|
27
|
+
```
|
28
|
+
|
29
|
+
### 3. Generate Uploadable Model
|
30
|
+
|
31
|
+
Use the PresignedUpload generator to create your uploadable model.
|
32
|
+
|
33
|
+
This model is intended to be a representation of access to a file, and also its basic informations.
|
34
|
+
|
35
|
+
For example, if you want create a UploadLink model, you can use:
|
36
|
+
```sh
|
37
|
+
rails g presigned_upload:uploadable_model UploadLink
|
38
|
+
```
|
39
|
+
If your database uses UUID for the primary and foreign keys, you can pass the `--uuid` option:
|
40
|
+
```sh
|
41
|
+
rails g presigned_upload:uploadable_model UploadLink --uuid
|
42
|
+
```
|
43
|
+
|
44
|
+
The generator will create the model with the given name and the migration file:
|
45
|
+
```ruby
|
46
|
+
# app/models/upload_link.rb
|
47
|
+
class UploadLink < ApplicationRecord
|
48
|
+
presigned_uploadable_model
|
49
|
+
end
|
50
|
+
|
51
|
+
# This columns are required, but you can add more if you need.
|
52
|
+
# For example, you can create a column called 'storage_size'
|
53
|
+
# to save the storage size of the file
|
54
|
+
class CreateUploadLinks < ActiveRecord::Migration[7.0]
|
55
|
+
def change
|
56
|
+
create_table :upload_links, id: :uuid do |t|
|
57
|
+
t.string :original_name, null: false
|
58
|
+
t.string :content_type, null: false
|
59
|
+
t.string :upload_status, null: false
|
60
|
+
|
61
|
+
t.timestamps
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
```
|
66
|
+
By default, an uploadable model contains:
|
67
|
+
- `original_name`: The original name of the file
|
68
|
+
- `content_type`: The content-type of the file _(Utile for validations)_
|
69
|
+
- `upload_status`: The status of the upload process [`initial`, `completed`]
|
70
|
+
|
71
|
+
### 4. Run the migration
|
72
|
+
|
73
|
+
```
|
74
|
+
rails db:migrate
|
75
|
+
```
|
76
|
+
|
20
77
|
## Usage
|
21
78
|
|
22
|
-
|
79
|
+
### Configuring initializer
|
80
|
+
|
81
|
+
You can configure the presigned_upload initializer to set the storage service and its options.
|
82
|
+
|
83
|
+
For AWS S3 for example, you going to need to add the `storage` to `:aws` and inform the `bucket` where you want to create presigned URLs in order to upload and access files:
|
84
|
+
```ruby
|
85
|
+
PresignedUpload.configure do |config|
|
86
|
+
config.storage = :aws
|
87
|
+
config.storage_options = {
|
88
|
+
bucket: 'my_bucket'
|
89
|
+
}
|
90
|
+
end
|
91
|
+
```
|
92
|
+
|
93
|
+
**Important:** For an AWS configuration, you need to add the [AWS SDK for Ruby](https://github.com/aws/aws-sdk-ruby) in order to `PresignedUpload` gem to work correctly:
|
94
|
+
```ruby
|
95
|
+
gem 'aws-sdk-s3'
|
96
|
+
```
|
97
|
+
If you don't add this, an error will be raised when trying to run Rails.
|
98
|
+
|
99
|
+
### Uploadable Model
|
100
|
+
|
101
|
+
The uploadable model will be used to access files in the cloud storage services. Below there are some of the methods that can be accessed:
|
102
|
+
```ruby
|
103
|
+
# Returns a presigned URL for uploading the file
|
104
|
+
# Only returns the URL if the upload_status is 'initial'
|
105
|
+
upload_link.upload_url
|
106
|
+
|
107
|
+
# Returns a presigned URL for accessing the stored file
|
108
|
+
# Only returns the URL if the upload_status is 'completed'
|
109
|
+
upload_link.url
|
110
|
+
|
111
|
+
# Deletes the stored file from the cloud
|
112
|
+
# This method is automatically called in before_destroy
|
113
|
+
# callback when you destroy the record
|
114
|
+
upload_link.delete_stored_file
|
115
|
+
```
|
116
|
+
|
117
|
+
In order to be able to use this methods, you need to configure the store directory where the file will be stored.
|
118
|
+
|
119
|
+
#### Storage path
|
120
|
+
|
121
|
+
By default, PresignedUpload inserts the method `presigned_uploadable_model` in your Model, that sets the value of the `store_dir` to: `"uploads/#{model_name.plural}/#{id}"`.
|
122
|
+
|
123
|
+
The final `store_path` of the file in the cloud will be the junction between `store_dir` + `original_name`.
|
124
|
+
|
125
|
+
So, in the example below, the file will be stored as `uploads/upload_links/123/my_file.txt`:
|
126
|
+
```ruby
|
127
|
+
class UploadLink < ApplicationRecord
|
128
|
+
presigned_uploadable_model
|
129
|
+
end
|
130
|
+
|
131
|
+
upload_link.id = '123'
|
132
|
+
upload_link.original_name = 'my_file.txt'
|
133
|
+
upload_link.store_path
|
134
|
+
# => uploads/upload_links/123/my_file.txt
|
135
|
+
```
|
136
|
+
|
137
|
+
#### Changing the storage directory
|
138
|
+
|
139
|
+
The `presigned_uploadable_model` method accepts the `:store_dir` option, that can be a `Symbol`, a `String` or a `Proc`.
|
140
|
+
|
141
|
+
Using a string:
|
142
|
+
```ruby
|
143
|
+
class UploadLink < ApplicationRecord
|
144
|
+
presigned_uploadable_model store_dir: '/uploads/my/custom/path'
|
145
|
+
end
|
146
|
+
|
147
|
+
upload_link.original_name = 'my_file.txt'
|
148
|
+
upload_link.store_path
|
149
|
+
# => "/uploads/my/custom/path/my_file.txt"
|
150
|
+
```
|
151
|
+
|
152
|
+
Using a Proc:
|
153
|
+
```ruby
|
154
|
+
class UploadLink < ApplicationRecord
|
155
|
+
presigned_uploadable_model store_dir: -> { "/uploads/upload_links/#{id}" }
|
156
|
+
end
|
157
|
+
|
158
|
+
upload_link.id = '123'
|
159
|
+
upload_link.original_name = 'my_file.txt'
|
160
|
+
upload_link.store_path
|
161
|
+
# => "/uploads/upload_links/123/my_file.txt"
|
162
|
+
```
|
163
|
+
|
164
|
+
Using a Symbol:
|
165
|
+
```ruby
|
166
|
+
class UploadLink < ApplicationRecord
|
167
|
+
presigned_uploadable_model store_dir: :my_custom_dir
|
168
|
+
|
169
|
+
# Using as a symbol, the code will expect a instance method with same name
|
170
|
+
def my_custom_dir
|
171
|
+
"/my/custom/dir/#{id}"
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
upload_link.id = '123'
|
176
|
+
upload_link.original_name = 'my_file.txt'
|
177
|
+
upload_link.store_path
|
178
|
+
# => "/my/custom/dir/123/my_file.txt"
|
179
|
+
```
|
23
180
|
|
24
181
|
## Contributing
|
25
182
|
|
26
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
183
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/denisstael/presigned_upload.
|
27
184
|
|
28
185
|
## License
|
29
186
|
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rails/generators"
|
4
|
+
|
5
|
+
module PresignedUpload
|
6
|
+
class InstallGenerator < Rails::Generators::Base
|
7
|
+
source_root File.expand_path("templates", __dir__)
|
8
|
+
|
9
|
+
def generate_initializer
|
10
|
+
template "initializer.rb", "config/initializers/presigned_upload.rb"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Use this initializer to configure PresignedUpload.
|
4
|
+
#
|
5
|
+
PresignedUpload.configure do |config|
|
6
|
+
# Configure the storage option to define which storage service will be used.
|
7
|
+
config.storage = :aws
|
8
|
+
|
9
|
+
# Configure the storage_options for additional information about the choosen storage service.
|
10
|
+
config.storage_options = {
|
11
|
+
bucket: 'my_bucket'
|
12
|
+
}
|
13
|
+
end
|
@@ -3,7 +3,6 @@ class Create<%= model_name.camelize.pluralize %> < ActiveRecord::Migration[7.0]
|
|
3
3
|
create_table :<%= model_name.underscore.pluralize + migration_id_column %> do |t|
|
4
4
|
t.string :original_name, null: false
|
5
5
|
t.string :content_type, null: false
|
6
|
-
t.string :store_path, null: false
|
7
6
|
t.string :upload_status, null: false
|
8
7
|
|
9
8
|
t.timestamps
|
@@ -17,10 +17,10 @@ module PresignedUpload
|
|
17
17
|
|
18
18
|
def configure!
|
19
19
|
unless AVAILABLE_STORAGES.include?(storage)
|
20
|
-
raise InvalidStorage, "Invalid storage. Allowed types are: #{AVAILABLE_STORAGES}"
|
20
|
+
raise InvalidStorage, "Invalid storage option. Allowed types are: #{AVAILABLE_STORAGES}"
|
21
21
|
end
|
22
22
|
|
23
|
-
raise InvalidStorageConfig, "Empty storage configuration" if storage_options.empty?
|
23
|
+
raise InvalidStorageConfig, "Empty storage options configuration" if storage_options.empty?
|
24
24
|
|
25
25
|
case storage
|
26
26
|
when :aws
|
@@ -31,33 +31,32 @@ module PresignedUpload
|
|
31
31
|
# all the behavior from the Uploadable module and the Uploadable::Model, including validations and callbacks
|
32
32
|
#
|
33
33
|
# @param [Hash] options Options hash
|
34
|
-
# @option options [Symbol] :
|
34
|
+
# @option options [Symbol] :store_dir The path to the storage location directory
|
35
35
|
# Accepts a Symbol value, which will try to call a method with same name
|
36
|
-
# in model, a String value representing the store
|
36
|
+
# in model, a String value representing the store directory or a Proc to call
|
37
37
|
#
|
38
38
|
# @example
|
39
39
|
#
|
40
40
|
# UploadLink < ApplicationRecord
|
41
|
-
# presigned_uploadable_model,
|
41
|
+
# presigned_uploadable_model, store_dir: :generate_store_dir
|
42
42
|
# end
|
43
43
|
#
|
44
44
|
def presigned_uploadable_model(options = {})
|
45
45
|
include Uploadable::Model
|
46
46
|
|
47
|
-
|
47
|
+
store_dir = options[:store_dir]
|
48
48
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
end
|
49
|
+
define_method :store_dir do
|
50
|
+
case store_dir
|
51
|
+
when Symbol
|
52
|
+
__send__(store_dir)
|
53
|
+
when String
|
54
|
+
store_dir
|
55
|
+
when Proc
|
56
|
+
instance_exec(&store_dir)
|
57
|
+
else
|
58
|
+
"uploads/#{model_name.plural}/#{id}"
|
59
|
+
end
|
61
60
|
end
|
62
61
|
end
|
63
62
|
# rubocop:enable Metrics/MethodLength
|
@@ -34,8 +34,6 @@ module PresignedUpload
|
|
34
34
|
include Uploadable
|
35
35
|
|
36
36
|
included do
|
37
|
-
attr_readonly :original_name, :content_type, :store_path
|
38
|
-
|
39
37
|
validates :original_name, :content_type, :upload_status, presence: true
|
40
38
|
|
41
39
|
enum upload_status: { initial: "initial", completed: "completed" }
|
@@ -54,14 +52,13 @@ module PresignedUpload
|
|
54
52
|
presigned_url(store_path, :get)
|
55
53
|
end
|
56
54
|
|
57
|
-
# Returns a presigned URL for uploading the file. Returns `nil` if the
|
58
|
-
# upload status is 'completed'.
|
55
|
+
# Returns a presigned URL for uploading the file. Returns `nil` if the upload status is 'completed'.
|
59
56
|
#
|
60
57
|
# @return [String, nil] The presigned URL for uploading the file or `nil` if the upload status is
|
61
|
-
#
|
58
|
+
# already marked as completed.
|
62
59
|
#
|
63
60
|
def upload_url
|
64
|
-
return if
|
61
|
+
return if completed?
|
65
62
|
|
66
63
|
presigned_url(store_path, :put)
|
67
64
|
end
|
@@ -71,6 +68,18 @@ module PresignedUpload
|
|
71
68
|
def delete_stored_file
|
72
69
|
delete_file(store_path)
|
73
70
|
end
|
71
|
+
|
72
|
+
# Returns the file store path.
|
73
|
+
#
|
74
|
+
# The store path is constructed by combining the storage directory
|
75
|
+
# with the `original_name` of the file. If `store_dir` is present, it is
|
76
|
+
# included in the path. If `store_dir` is not present, then the file
|
77
|
+
# will be saved in the root directory.
|
78
|
+
#
|
79
|
+
# @return [String] the storage path for the uploaded file.
|
80
|
+
def store_path
|
81
|
+
"#{store_dir.present? ? "#{store_dir}/" : ""}#{original_name}"
|
82
|
+
end
|
74
83
|
end
|
75
84
|
end
|
76
85
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: presigned_upload
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Denis Stael
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-07-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|
@@ -52,8 +52,8 @@ dependencies:
|
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: 1.4.2
|
55
|
-
description: ' "
|
56
|
-
|
55
|
+
description: ' "Handle direct file uploads to cloud storage services via presigned
|
56
|
+
URLs."
|
57
57
|
|
58
58
|
'
|
59
59
|
email:
|
@@ -70,6 +70,8 @@ files:
|
|
70
70
|
- LICENSE.txt
|
71
71
|
- README.md
|
72
72
|
- Rakefile
|
73
|
+
- lib/generators/presigned_upload/install_generator.rb
|
74
|
+
- lib/generators/presigned_upload/templates/initializer.rb
|
73
75
|
- lib/generators/presigned_upload/templates/migration.rb
|
74
76
|
- lib/generators/presigned_upload/templates/uploadable_model.rb
|
75
77
|
- lib/generators/presigned_upload/uploadable_model_generator.rb
|
@@ -83,12 +85,13 @@ files:
|
|
83
85
|
- lib/presigned_upload/storage.rb
|
84
86
|
- lib/presigned_upload/uploadable.rb
|
85
87
|
- lib/presigned_upload/version.rb
|
86
|
-
homepage: https://github.com/
|
88
|
+
homepage: https://github.com/denisstael/presigned_upload
|
87
89
|
licenses:
|
88
90
|
- MIT
|
89
91
|
metadata:
|
90
|
-
homepage_uri: https://github.com/
|
91
|
-
source_code_uri: https://github.com/
|
92
|
+
homepage_uri: https://github.com/denisstael/presigned_upload
|
93
|
+
source_code_uri: https://github.com/denisstael/presigned_upload
|
94
|
+
documentation_uri: https://rubydoc.info/gems/presigned_upload/0.2.0
|
92
95
|
post_install_message:
|
93
96
|
rdoc_options: []
|
94
97
|
require_paths:
|
@@ -107,6 +110,6 @@ requirements: []
|
|
107
110
|
rubygems_version: 3.3.7
|
108
111
|
signing_key:
|
109
112
|
specification_version: 4
|
110
|
-
summary: Control model associated files uploads via presigned
|
113
|
+
summary: Control model associated files uploads via presigned URLs to cloud storage
|
111
114
|
services.
|
112
115
|
test_files: []
|