presigned_upload 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 651464bf9650c6acb0cbd6da5f0d3e1cb41bcf16c2825e32a64fd102e214917b
4
+ data.tar.gz: e07b382fd743b259a3196912dbb19eff4069e55381e20eb7b9a20f622b20aa88
5
+ SHA512:
6
+ metadata.gz: 8b7f57130a706d1d5651652134b152cf51a4dcd6442382c8c608d22d2b3af1380048f6bca891a811559cc94f7bc892a017170962ea23f92f6d2149ccf98ea066
7
+ data.tar.gz: 9329538342ea4ae88cd2b571e92c6cc87512a2f8f455cf6332267768ebdc9d170da6daed49bad15ad276cf77d801d3210076b82fdb07c1bbaae1dae17598fded
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,28 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.6
3
+ Exclude:
4
+ - "lib/generators/**/templates/*"
5
+ NewCops: disable
6
+
7
+ Style/Documentation:
8
+ Enabled: true
9
+ Exclude:
10
+ - "lib/generators/presigned_upload/**"
11
+ - "spec/support/storage/*"
12
+ - "spec/support/*"
13
+
14
+ Style/StringLiterals:
15
+ Enabled: true
16
+ EnforcedStyle: double_quotes
17
+
18
+ Style/StringLiteralsInInterpolation:
19
+ Enabled: true
20
+ EnforcedStyle: double_quotes
21
+
22
+ Layout/LineLength:
23
+ Max: 120
24
+
25
+ Metrics/BlockLength:
26
+ Enabled: true
27
+ Exclude:
28
+ - "spec/**/*"
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2023-11-23
4
+
5
+ - Initial release
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in presigned_upload.gemspec
6
+ gemspec
7
+
8
+ gem "rake", "~> 13.0"
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2023 Denis Stael
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,30 @@
1
+ # PresignedUpload
2
+
3
+ Gem designed to control file uploads via presigned URLs to cloud storage services.
4
+
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
+
7
+ ## Installation
8
+
9
+ Add this to your Gemfile and run `bundle install`.
10
+
11
+ ```ruby
12
+ gem 'presigned_upload'
13
+ ```
14
+
15
+ Or install it yourself as:
16
+ ```sh
17
+ gem install presigned_upload
18
+ ```
19
+
20
+ ## Usage
21
+
22
+ TODO: Write usage instructions here
23
+
24
+ ## Contributing
25
+
26
+ Bug reports and pull requests are welcome on GitHub at https://github.com/DenisStael/presigned_upload.
27
+
28
+ ## License
29
+
30
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ require "rubocop/rake_task"
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ task default: %i[spec rubocop]
@@ -0,0 +1,12 @@
1
+ class Create<%= model_name.camelize.pluralize %> < ActiveRecord::Migration[7.0]
2
+ def change
3
+ create_table :<%= model_name.underscore.pluralize + migration_id_column %> do |t|
4
+ t.string :original_name, null: false
5
+ t.string :content_type, null: false
6
+ t.string :store_path, null: false
7
+ t.string :upload_status, null: false
8
+
9
+ t.timestamps
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,3 @@
1
+ class <%= model_name.camelize %> < ApplicationRecord
2
+ presigned_uploadable_model
3
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators"
4
+ require "rails/generators/active_record"
5
+
6
+ module PresignedUpload
7
+ class UploadableModelGenerator < Rails::Generators::Base
8
+ include Rails::Generators::Migration
9
+
10
+ source_root File.expand_path("templates", __dir__)
11
+
12
+ argument :model_name, type: :string, desc: "Name of presigned uploadable model"
13
+ class_option :uuid, type: :boolean, default: false, desc: "Use UUID type for primary key"
14
+
15
+ def self.next_migration_number(path)
16
+ ActiveRecord::Generators::Base.next_migration_number(path)
17
+ end
18
+
19
+ def create_model_and_migration
20
+ generate_model
21
+ generate_migration
22
+ end
23
+
24
+ private
25
+
26
+ def generate_model
27
+ template "uploadable_model.rb", File.join("app", "models", "#{model_name.underscore}.rb")
28
+ end
29
+
30
+ def migration_id_column
31
+ options.uuid? ? ", id: :uuid" : ""
32
+ end
33
+
34
+ def generate_migration
35
+ migration_template "migration.rb", "db/migrate/create_#{model_name.underscore.pluralize}.rb"
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "presigned_upload/adapter/base"
4
+
5
+ module PresignedUpload
6
+ module Adapter
7
+ #
8
+ # The `Aws` class is an adapter for interacting with the Amazon Simple Storage Service (S3) using the AWS SDK.
9
+ # It inherits from `Adapter::Base` and implements methods for generating presigned URLs, uploading files,
10
+ # and deleting files.
11
+ #
12
+ # @see Adapter::Base
13
+ class Aws < Base
14
+ #
15
+ # Generates a presigned URL for the specified key, HTTP method, and expiration time.
16
+ #
17
+ # @param [String] key The key or identifier of the file in the storage.
18
+ # @param [Symbol] method The HTTP method for which the presigned URL is generated (e.g., :put, :get).
19
+ # @param [Integer] expires_in The duration in seconds for which the URL is valid.
20
+ #
21
+ # @return [String] The presigned URL for the specified key, method, and expiration time.
22
+ def presigned_url(key, method, expires_in)
23
+ bucket.object(key).presigned_url(method, expires_in: expires_in)
24
+ end
25
+
26
+ # Uploads the specified file to the storage with the given key.
27
+ #
28
+ # @param [String] file The path to the local file to be uploaded.
29
+ # @param [String] key The key or identifier for the file in the storage.
30
+ #
31
+ # @return [void]
32
+ def upload_file(file, key)
33
+ object = bucket.object(key)
34
+ object.upload_file(file)
35
+ end
36
+
37
+ # Deletes the file with the specified key from the storage.
38
+ #
39
+ # @param [String] key The key or identifier of the file to be deleted.
40
+ #
41
+ # @return [void]
42
+ def delete_file(key)
43
+ bucket.object(key).delete
44
+ end
45
+
46
+ private
47
+
48
+ # Returns the AWS S3 client for interacting with the service.
49
+ #
50
+ # @return [Aws::S3::Client] The AWS S3 client.
51
+ def client
52
+ @client ||= ::Aws::S3::Client.new
53
+ end
54
+
55
+ # Returns the AWS S3 resource for interacting with S3 objects and buckets.
56
+ #
57
+ # @return [Aws::S3::Resource] The AWS S3 resource.
58
+ def resource
59
+ @resource ||= ::Aws::S3::Resource.new
60
+ end
61
+
62
+ # Returns the name of the S3 bucket configured for storage.
63
+ #
64
+ # @return [String] The name of the S3 bucket.
65
+ def bucket_name
66
+ @bucket_name ||= storage_options[:bucket]
67
+ end
68
+
69
+ # Returns the S3 bucket resource for the configured bucket name.
70
+ #
71
+ # @return [Aws::S3::Bucket] The S3 bucket resource.
72
+ def bucket
73
+ @bucket ||= resource.bucket(bucket_name)
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PresignedUpload
4
+ #
5
+ # The `Adapter` module contains classes responsible for interfacing with different storage services.
6
+ # Each adapter class should inherit from `PresignedUpload::Adapter::Base`.
7
+ #
8
+ # @see PresignedUpload::Adapter::Base
9
+ module Adapter
10
+ # The `Base` class serves as the base class for all storage adapters in the `PresignedUpload` module.
11
+ # It provides a common interface for accessing storage configurations.
12
+ #
13
+ # @see PresignedUpload::Adapter
14
+ class Base
15
+ #
16
+ # Initializes a new instance of the adapter.
17
+ #
18
+ def initialize
19
+ @storage_options = PresignedUpload.configuration.storage_options
20
+ end
21
+
22
+ protected
23
+
24
+ # Returns the storage configuration for the adapter.
25
+ #
26
+ # @return [Hash] The storage configuration.
27
+ attr_reader :storage_options
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PresignedUpload
4
+ #
5
+ # Configuration class
6
+ #
7
+ class Configuration
8
+ AVAILABLE_STORAGES = [:aws].freeze
9
+
10
+ attr_accessor :storage, :storage_options
11
+ attr_reader :adapter_class
12
+
13
+ def initialize
14
+ @storage = nil
15
+ @storage_options = {}
16
+ end
17
+
18
+ def configure!
19
+ unless AVAILABLE_STORAGES.include?(storage)
20
+ raise InvalidStorage, "Invalid storage. Allowed types are: #{AVAILABLE_STORAGES}"
21
+ end
22
+
23
+ raise InvalidStorageConfig, "Empty storage configuration" if storage_options.empty?
24
+
25
+ case storage
26
+ when :aws
27
+ load_aws
28
+ end
29
+
30
+ self
31
+ end
32
+
33
+ private
34
+
35
+ def load_aws
36
+ require_aws_dependencies!
37
+
38
+ required_config_keys = [:bucket]
39
+ storage_options.each_key { |key| storage_options.except!(key) unless required_config_keys.include?(key) }
40
+
41
+ unless required_config_keys.all? { |key| storage_options.key?(key) }
42
+ raise InvalidStorageConfig, "Missing storage configuration. Required keys are: #{required_config_keys}"
43
+ end
44
+
45
+ @adapter_class = PresignedUpload::Adapter::Aws
46
+ end
47
+
48
+ def require_aws_dependencies!
49
+ unless defined?(::Aws::S3)
50
+ raise MissingDependency, "Missing required dependency\n
51
+ PresignedUpload requires 'aws-sdk-s3' gem to work properly with Aws S3 storage.\n
52
+ To fix this error, consider adding [gem 'aws-sdk-s3'] into your Gemfile."
53
+ end
54
+
55
+ require "presigned_upload/adapter/aws"
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PresignedUpload
4
+ class InvalidStorage < StandardError; end
5
+ class InvalidStorageConfig < StandardError; end
6
+ class MissingDependency < StandardError; end
7
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "presigned_upload/uploadable"
4
+
5
+ module PresignedUpload
6
+ #
7
+ # Module extended by ActiveRecord::Base to allow configuring models with resources of the gem
8
+ #
9
+ module Models
10
+ #
11
+ # Calling this method in the context of the model class includes Uploadable module, which makes
12
+ # available the methods for requesting presigned_urls and deleting file from the cloud storage
13
+ #
14
+ # @example:
15
+ #
16
+ # UploadLink < ApplicationRecord
17
+ # presigned_uploadable
18
+ # end
19
+ #
20
+ # upload_link = UploadLink.new
21
+ # upload_link.presigned_url('key', :get, expires_in = 3600)
22
+ # upload_link.delete_file('key')
23
+ #
24
+ def presigned_uploadable
25
+ include Uploadable
26
+ end
27
+
28
+ # rubocop:disable Metrics/MethodLength
29
+ #
30
+ # Calling this method in the context of the model class includes Uploadable::Model module, which includes
31
+ # all the behavior from the Uploadable module and the Uploadable::Model, including validations and callbacks
32
+ #
33
+ # @param [Hash] options Options hash
34
+ # @option options [Symbol] :store_path The path to the storage location
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
37
+ #
38
+ # @example
39
+ #
40
+ # UploadLink < ApplicationRecord
41
+ # presigned_uploadable_model, store_path: :generate_store_path
42
+ # end
43
+ #
44
+ def presigned_uploadable_model(options = {})
45
+ include Uploadable::Model
46
+
47
+ store_path = options[:store_path]
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
61
+ end
62
+ end
63
+ # rubocop:enable Metrics/MethodLength
64
+ end
65
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/railtie"
4
+ require "presigned_upload/models"
5
+
6
+ # rubocop:disable Style/Documentation
7
+
8
+ module PresignedUpload
9
+ class Railtie < Rails::Railtie
10
+ initializer "presigned_upload.initialize" do
11
+ ActiveSupport.on_load(:active_record) do
12
+ ActiveRecord::Base.extend PresignedUpload::Models
13
+ end
14
+ end
15
+ end
16
+ end
17
+
18
+ # rubocop:enable Style/Documentation
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PresignedUpload
4
+ # Storage module that provides a common interface for interacting with different storage adapters.
5
+ #
6
+ # @example
7
+ # class MyUploader
8
+ # include Storage
9
+ # end
10
+ #
11
+ # uploader = MyUploader.new
12
+ # url = uploader.presigned_url('key', :put, 3600)
13
+ # uploader.delete_file('key')
14
+ #
15
+ module Storage
16
+ # Generates a presigned URL for the specified key, HTTP method, and expiration time.
17
+ #
18
+ # @param [String] key The key or identifier of the file in the storage.
19
+ # @param [Symbol] method The HTTP method for which the presigned URL is generated (e.g., :put, :get).
20
+ # @param [Integer] expires_in The duration in seconds for which the URL is valid (default is 3600 seconds).
21
+ #
22
+ # @return [String] The presigned URL for the specified key and method.
23
+ def presigned_url(key, method, expires_in = 3600)
24
+ adapter.presigned_url(key, method, expires_in)
25
+ end
26
+
27
+ # Deletes the file with the specified key from the storage.
28
+ #
29
+ # @param [String] key The key or identifier of the file to be deleted.
30
+ #
31
+ def delete_file(key)
32
+ adapter.delete_file(key)
33
+ end
34
+
35
+ private
36
+
37
+ # Returns the storage adapter based on the configured storage type.
38
+ #
39
+ # @return [PresignedUpload::Adapter::Base] An instance of the configured storage adapter.
40
+ def adapter
41
+ @adapter ||= PresignedUpload.adapter_class.new
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "presigned_upload/storage"
4
+ require "active_support/concern"
5
+
6
+ module PresignedUpload
7
+ #
8
+ # The `Uploadable` module integrates storage functionality with a model, allowing handling
9
+ # file uploads and associated storage operations. It includes the capabilities provided by the
10
+ # `PresignedUpload::Storage` module.
11
+ #
12
+ # This module is designed to be included in ActiveRecord models to facilitate common file upload
13
+ # operations, such as generating presigned URLs, deleting stored files, and managing upload status.
14
+ #
15
+ # @example
16
+ #
17
+ # class MyUploadableModel < ApplicationRecord
18
+ # include Uploadable::Model
19
+ # end
20
+ #
21
+ # model = MyUploadableModel.new(original_name: 'example.txt', content_type: 'text/plain', upload_status: :initial)
22
+ # model.upload_url # Generates a presigned URL for file upload.
23
+ # model.url # Retrieves a presigned URL for accessing the stored file.
24
+ #
25
+ # @see PresignedUpload::Storage
26
+ module Uploadable
27
+ include PresignedUpload::Storage
28
+
29
+ # The `Model` submodule extends ActiveSupport::Concern to provide upload-related functionality
30
+ # to ActiveRecord models.
31
+ module Model
32
+ extend ActiveSupport::Concern
33
+
34
+ include Uploadable
35
+
36
+ included do
37
+ attr_readonly :original_name, :content_type, :store_path
38
+
39
+ validates :original_name, :content_type, :upload_status, presence: true
40
+
41
+ enum upload_status: { initial: "initial", completed: "completed" }
42
+
43
+ after_destroy { delete_stored_file }
44
+ end
45
+
46
+ # Returns a presigned URL for accessing the stored file. Returns `nil` if the upload status is not 'completed'.
47
+ #
48
+ # @return [String, nil] The presigned URL for accessing the stored file or `nil` if the
49
+ # upload status is not completed.
50
+ #
51
+ def url
52
+ return unless completed?
53
+
54
+ presigned_url(store_path, :get)
55
+ end
56
+
57
+ # Returns a presigned URL for uploading the file. Returns `nil` if the store path is blank or the
58
+ # upload status is 'completed'.
59
+ #
60
+ # @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.
62
+ #
63
+ def upload_url
64
+ return if store_path.blank? || completed?
65
+
66
+ presigned_url(store_path, :put)
67
+ end
68
+
69
+ # Deletes the stored file associated with the model.
70
+ #
71
+ def delete_stored_file
72
+ delete_file(store_path)
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PresignedUpload
4
+ VERSION = "0.1.1"
5
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "presigned_upload/railtie"
4
+ require "presigned_upload/version"
5
+ require "presigned_upload/configuration"
6
+ require "presigned_upload/errors"
7
+ require "presigned_upload/adapter/base"
8
+
9
+ # The `PresignedUpload` module serves as the main module for the presigned file upload functionality.
10
+ # It provides configuration options, such as storage type and configuration settings.
11
+ #
12
+ module PresignedUpload
13
+ class << self
14
+ attr_accessor :configuration
15
+
16
+ def configure
17
+ self.configuration ||= Configuration.new
18
+
19
+ yield(configuration)
20
+
21
+ configuration.configure!
22
+ end
23
+ end
24
+
25
+ def self.adapter_class
26
+ @adapter_class ||= configuration.adapter_class
27
+ end
28
+ end
metadata ADDED
@@ -0,0 +1,111 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: presigned_upload
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Denis Stael
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-11-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rubocop
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.21'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.21'
41
+ - !ruby/object:Gem::Dependency
42
+ name: sqlite3
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 1.4.2
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
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."
57
+
58
+ '
59
+ email:
60
+ - denissantistael@gmail.com
61
+ executables: []
62
+ extensions: []
63
+ extra_rdoc_files: []
64
+ files:
65
+ - ".rspec"
66
+ - ".rubocop.yml"
67
+ - CHANGELOG.md
68
+ - Gemfile
69
+ - LICENSE.txt
70
+ - README.md
71
+ - Rakefile
72
+ - lib/generators/presigned_upload/templates/migration.rb
73
+ - lib/generators/presigned_upload/templates/uploadable_model.rb
74
+ - lib/generators/presigned_upload/uploadable_model_generator.rb
75
+ - lib/presigned_upload.rb
76
+ - lib/presigned_upload/adapter/aws.rb
77
+ - lib/presigned_upload/adapter/base.rb
78
+ - lib/presigned_upload/configuration.rb
79
+ - lib/presigned_upload/errors.rb
80
+ - lib/presigned_upload/models.rb
81
+ - lib/presigned_upload/railtie.rb
82
+ - lib/presigned_upload/storage.rb
83
+ - lib/presigned_upload/uploadable.rb
84
+ - lib/presigned_upload/version.rb
85
+ homepage: https://github.com/DenisStael/presigned_upload
86
+ licenses:
87
+ - MIT
88
+ metadata:
89
+ homepage_uri: https://github.com/DenisStael/presigned_upload
90
+ source_code_uri: https://github.com/DenisStael/presigned_upload
91
+ post_install_message:
92
+ rdoc_options: []
93
+ require_paths:
94
+ - lib
95
+ required_ruby_version: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ version: 2.6.0
100
+ required_rubygems_version: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ requirements: []
106
+ rubygems_version: 3.3.7
107
+ signing_key:
108
+ specification_version: 4
109
+ summary: Control model associated files uploads via presigned URL to cloud storage
110
+ services.
111
+ test_files: []