presigned_upload 0.1.1 → 0.2.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.
- checksums.yaml +4 -4
- data/.rubocop.yml +5 -0
- data/.yardopts +1 -0
- 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 +16 -17
- data/lib/presigned_upload/railtie.rb +1 -5
- data/lib/presigned_upload/uploadable.rb +15 -6
- data/lib/presigned_upload/version.rb +1 -1
- metadata +12 -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/.rubocop.yml
CHANGED
data/.yardopts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--exclude lib/generators/presigned_upload/templates/
|
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
|
@@ -26,38 +26,37 @@ module PresignedUpload
|
|
26
26
|
end
|
27
27
|
|
28
28
|
# rubocop:disable Metrics/MethodLength
|
29
|
-
|
29
|
+
|
30
30
|
# Calling this method in the context of the model class includes Uploadable::Model module, which includes
|
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
|
@@ -3,10 +3,8 @@
|
|
3
3
|
require "rails/railtie"
|
4
4
|
require "presigned_upload/models"
|
5
5
|
|
6
|
-
# rubocop:disable Style/Documentation
|
7
|
-
|
8
6
|
module PresignedUpload
|
9
|
-
class Railtie < Rails::Railtie
|
7
|
+
class Railtie < Rails::Railtie # :nodoc:
|
10
8
|
initializer "presigned_upload.initialize" do
|
11
9
|
ActiveSupport.on_load(:active_record) do
|
12
10
|
ActiveRecord::Base.extend PresignedUpload::Models
|
@@ -14,5 +12,3 @@ module PresignedUpload
|
|
14
12
|
end
|
15
13
|
end
|
16
14
|
end
|
17
|
-
|
18
|
-
# rubocop:enable Style/Documentation
|
@@ -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:
|
@@ -64,11 +64,14 @@ extra_rdoc_files: []
|
|
64
64
|
files:
|
65
65
|
- ".rspec"
|
66
66
|
- ".rubocop.yml"
|
67
|
+
- ".yardopts"
|
67
68
|
- CHANGELOG.md
|
68
69
|
- Gemfile
|
69
70
|
- LICENSE.txt
|
70
71
|
- README.md
|
71
72
|
- Rakefile
|
73
|
+
- lib/generators/presigned_upload/install_generator.rb
|
74
|
+
- lib/generators/presigned_upload/templates/initializer.rb
|
72
75
|
- lib/generators/presigned_upload/templates/migration.rb
|
73
76
|
- lib/generators/presigned_upload/templates/uploadable_model.rb
|
74
77
|
- lib/generators/presigned_upload/uploadable_model_generator.rb
|
@@ -82,12 +85,13 @@ files:
|
|
82
85
|
- lib/presigned_upload/storage.rb
|
83
86
|
- lib/presigned_upload/uploadable.rb
|
84
87
|
- lib/presigned_upload/version.rb
|
85
|
-
homepage: https://github.com/
|
88
|
+
homepage: https://github.com/denisstael/presigned_upload
|
86
89
|
licenses:
|
87
90
|
- MIT
|
88
91
|
metadata:
|
89
|
-
homepage_uri: https://github.com/
|
90
|
-
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
|
91
95
|
post_install_message:
|
92
96
|
rdoc_options: []
|
93
97
|
require_paths:
|
@@ -106,6 +110,6 @@ requirements: []
|
|
106
110
|
rubygems_version: 3.3.7
|
107
111
|
signing_key:
|
108
112
|
specification_version: 4
|
109
|
-
summary: Control model associated files uploads via presigned
|
113
|
+
summary: Control model associated files uploads via presigned URLs to cloud storage
|
110
114
|
services.
|
111
115
|
test_files: []
|