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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: db1a9758dacddbb8efeffaf2b99b13c08fca378a82f93a628bba3b7ee38c5c3b
4
- data.tar.gz: 93f42f25157570cdf9b036f513c53a588c3453c86777fe6157d58ec2b943a6c3
3
+ metadata.gz: ca293e74541ff82d393ac583e295699df5f5fbf80d32c2b4f4365fc820e76959
4
+ data.tar.gz: 75e337709fe872cf5f5fc71470017f37e5b6dc5266152ae166cec6d29fcc58bc
5
5
  SHA512:
6
- metadata.gz: d5f58a70348ab99eb0ed36adcbca0d3d8f81336b76ea8c7206a9fc2cb4d9b4b4d0c215ecebcf4064ba722f694ce4be535bfcd45c3dc87446ad41f46b2ef9845b
7
- data.tar.gz: 9e37103e3febbb2056ddadda8307d597d82c751d9fcd10de96eb4ec82fcdc9648fd80a688ef13bce0c72b632d6c9db93eca505a894f1fdc901c5aca04c08bb53
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
- Gem designed to control file uploads via presigned URLs to cloud storage services.
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
- TODO: Write usage instructions here
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/DenisStael/presigned_upload.
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] :store_path The path to the storage location
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 path or a Proc to call
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, store_path: :generate_store_path
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
- store_path = options[:store_path]
47
+ store_dir = options[:store_dir]
48
48
 
49
- before_create do
50
- self.store_path =
51
- case store_path
52
- when Symbol
53
- __send__(store_path)
54
- when String
55
- store_path
56
- when Proc
57
- store_path.call
58
- else
59
- "uploads/#{model_name.plural}/#{original_name}"
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 store path is blank or 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
- # 'completed' or store path is not present.
58
+ # already marked as completed.
62
59
  #
63
60
  def upload_url
64
- return if store_path.blank? || completed?
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PresignedUpload
4
- VERSION = "0.1.2"
4
+ VERSION = "0.2.0"
5
5
  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.1.2
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: 2023-12-01 00:00:00.000000000 Z
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: ' "This gem provides an easy way to handle direct file uploads to
56
- cloud storage services via presigned URL."
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/DenisStael/presigned_upload
88
+ homepage: https://github.com/denisstael/presigned_upload
87
89
  licenses:
88
90
  - MIT
89
91
  metadata:
90
- homepage_uri: https://github.com/DenisStael/presigned_upload
91
- source_code_uri: https://github.com/DenisStael/presigned_upload
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 URL to cloud storage
113
+ summary: Control model associated files uploads via presigned URLs to cloud storage
111
114
  services.
112
115
  test_files: []