fileboost 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: fd85f01129f867dfe1627a2fd20f22ac47e873f03cdecf5973ffa1cbc0f3ff40
4
+ data.tar.gz: e2f625239f1e930f3d08b10f8fd67a62b882c553291e112bcd34ce0b1b92598e
5
+ SHA512:
6
+ metadata.gz: c206e01371679f502dac87f79309933e38560e76eba829f01016458e6cf2588ef6167df9c737ca4caee636406795647536428e96ba8899b12a2c07f6221e57df
7
+ data.tar.gz: cfd0ad1902c05576a38e34d4fe5aec67ce0c1a5349f26bd044fd840ae83ba7cac7ad71d693b083b9581cc35c68a2386eec39114c0a1b05f0fb5de06ac7b7e7c4
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright bilal
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,227 @@
1
+ # Fileboost
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/fileboost.svg)](https://badge.fury.io/rb/fileboost)
4
+
5
+ Fileboost is a Rails gem that provides seamless integration with the Fileboost.dev image optimization service. It offers drop-in replacement helpers for Rails' native image helpers with automatic optimization, HMAC authentication, and comprehensive transformation support for ActiveStorage objects.
6
+
7
+ ## Features
8
+
9
+ - 🚀 **Drop-in replacement** for Rails `image_tag` and `url_for` helpers
10
+ - 🔒 **Secure HMAC authentication** with Fileboost.dev service
11
+ - 📱 **ActiveStorage only** - works exclusively with ActiveStorage attachments
12
+ - 🎛️ **Comprehensive transformations** - resize, quality, format conversion, and more
13
+ - 🔧 **Simple configuration** - just project ID and token required
14
+
15
+ ## Installation
16
+
17
+ Register an account at [Fileboost.dev](https://fileboost.dev) and obtain your project ID and token.
18
+
19
+ Add this line to your application's Gemfile:
20
+
21
+ ```ruby
22
+ gem "fileboost"
23
+ ```
24
+
25
+ And then execute:
26
+
27
+ ```bash
28
+ $ bundle install
29
+ ```
30
+
31
+ Generate the initializer:
32
+
33
+ ```bash
34
+ $ rails generate fileboost:install
35
+ ```
36
+
37
+ ## Configuration
38
+
39
+ Set your environment variables:
40
+
41
+ ```bash
42
+ export FILEBOOST_PROJECT_ID="your-project-id"
43
+ export FILEBOOST_TOKEN="your-secret-token"
44
+ ```
45
+
46
+ ## Usage
47
+
48
+ ### Basic Image Tag
49
+
50
+ Replace `image_tag` with `fileboost_image_tag` for ActiveStorage objects:
51
+
52
+ ```erb
53
+ <!-- Before (Rails) -->
54
+ <%= image_tag user.avatar, width: 300, height: 300, alt: "Avatar" %>
55
+
56
+ <!-- After (Fileboost) -->
57
+ <%= fileboost_image_tag user.avatar, resize: { w: 300, h: 300 }, alt: "Avatar" %>
58
+ ```
59
+
60
+ **Note:** Fileboost only works with ActiveStorage objects. String paths and external URLs are not supported.
61
+
62
+ ### URL Generation
63
+
64
+ Generate optimized URLs directly:
65
+
66
+ ```erb
67
+ <div style="background-image: url(<%= fileboost_url_for(banner.image, resize: { w: 1200, h: 400 }) %>)">
68
+ <!-- content -->
69
+ </div>
70
+ ```
71
+
72
+ ### Transformation Options
73
+
74
+ Fileboost supports comprehensive image transformations:
75
+
76
+ ```erb
77
+ <%= fileboost_image_tag post.image,
78
+ resize: {
79
+ width: 800, # Resize width
80
+ height: 600, # Resize height
81
+ quality: 85, # JPEG/WebP quality (1-100)
82
+ blur: 5, # Blur effect (0-100)
83
+ brightness: 110, # Brightness adjustment (0-200, 100 = normal)
84
+ contrast: 120, # Contrast adjustment (0-200, 100 = normal)
85
+ rotation: 90, # Rotation in degrees (0-359)
86
+ fit: :cover # Resize behavior (cover, contain, fill, scale-down, crop, pad)
87
+ },
88
+ class: "hero-image", # Standard Rails options work too
89
+ alt: "Hero image" %>
90
+
91
+ <!-- Short parameter names also work -->
92
+ <%= fileboost_image_tag post.image,
93
+ resize: { w: 800, h: 600, q: 85 },
94
+ class: "hero-image" %>
95
+ ```
96
+
97
+ ### Parameter Aliases
98
+
99
+ Use short or long parameter names within the resize parameter:
100
+
101
+ ```ruby
102
+ # These are equivalent:
103
+ fileboost_image_tag(image, resize: { w: 400, h: 300, q: 85 })
104
+ fileboost_image_tag(image, resize: { width: 400, height: 300, quality: 85 })
105
+ ```
106
+
107
+ **Note:** Avoid using the `format` parameter. Fileboost automatically selects the optimal image format (WebP, AVIF, JPEG, etc.) based on browser headers and capabilities for the best performance and compatibility.
108
+
109
+ ### ActiveStorage Support
110
+
111
+ Works seamlessly with all ActiveStorage attachment types:
112
+
113
+ ```erb
114
+ <!-- has_one_attached -->
115
+ <%= fileboost_image_tag user.avatar, resize: { w: 150, h: 150, fit: :cover } %>
116
+
117
+ <!-- has_many_attached -->
118
+ <% post.images.each do |image| %>
119
+ <%= fileboost_image_tag image, resize: { width: 400, quality: 90 } %>
120
+ <% end %>
121
+
122
+ <!-- Direct blob access -->
123
+ <%= fileboost_image_tag post.featured_image.blob, resize: { w: 800 } %>
124
+ ```
125
+
126
+ ### Responsive Images
127
+
128
+ Generate multiple sizes for responsive designs:
129
+
130
+ ```ruby
131
+ # In your controller or helper
132
+ @responsive_urls = fileboost_responsive_urls(hero.image, [
133
+ { width: 400, suffix: "sm" },
134
+ { width: 800, suffix: "md" },
135
+ { width: 1200, suffix: "lg" }
136
+ ], resize: { quality: 85 })
137
+
138
+ # Returns: { "sm" => "url1", "md" => "url2", "lg" => "url3" }
139
+ ```
140
+
141
+ ```erb
142
+ <!-- In your view -->
143
+ <img src="<%= @responsive_urls['md'] %>"
144
+ srcset="<%= @responsive_urls['sm'] %> 400w,
145
+ <%= @responsive_urls['md'] %> 800w,
146
+ <%= @responsive_urls['lg'] %> 1200w"
147
+ sizes="(max-width: 400px) 400px, (max-width: 800px) 800px, 1200px"
148
+ alt="Responsive image">
149
+ ```
150
+
151
+ ## Error Handling
152
+
153
+ Fileboost handles errors gracefully:
154
+
155
+ - **Configuration errors**: Logs warnings about missing configuration and returns empty strings/nil
156
+ - **Invalid assets**: Logs errors when non-ActiveStorage objects are passed and returns empty strings/nil
157
+ - **Signature errors**: Returns nil when HMAC generation fails
158
+
159
+ ## Security
160
+
161
+ Fileboost uses HMAC-SHA256 signatures to secure your image transformations:
162
+
163
+ - URLs are signed with your secret token
164
+ - Prevents unauthorized image manipulation
165
+ - Signatures include all transformation parameters
166
+ - Uses secure comparison to prevent timing attacks
167
+
168
+ ## Development
169
+
170
+ After checking out the repo, run:
171
+
172
+ ```bash
173
+ $ bundle install
174
+ $ rake test
175
+ ```
176
+
177
+ To test against the dummy Rails application:
178
+
179
+ ```bash
180
+ $ cd test/dummy
181
+ $ rails server
182
+ ```
183
+
184
+ ## Testing
185
+
186
+ Run the test suite:
187
+
188
+ ```bash
189
+ $ rake test
190
+ ```
191
+
192
+ Run RuboCop:
193
+
194
+ ```bash
195
+ $ bundle exec rubocop
196
+ ```
197
+
198
+ ## Contributing
199
+
200
+ 1. Fork it
201
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
202
+ 3. Write tests for your changes
203
+ 4. Ensure all tests pass (`rake test`)
204
+ 5. Commit your changes (`git commit -am 'Add some feature'`)
205
+ 6. Push to the branch (`git push origin my-new-feature`)
206
+ 7. Create new Pull Request
207
+
208
+ ## Fileboost.dev Service
209
+
210
+ To use Fileboost, you'll need access to the Fileboost.dev image optimization service at `cdn.fileboost.dev`. The service:
211
+
212
+ 1. Receives requests at `https://cdn.fileboost.dev/{project_id}/path/to/activestorage/blob`
213
+ 2. Verifies HMAC signatures using your secret token
214
+ 3. Applies transformations based on query parameters
215
+ 4. Returns optimized images
216
+
217
+ The service handles ActiveStorage blob URLs and applies image transformations on-the-fly.
218
+
219
+ ## License
220
+
221
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
222
+
223
+ ## Support
224
+
225
+ - [GitHub Issues](https://github.com/bilalbudhani/fileboost/issues)
226
+ - [Documentation](https://github.com/bilalbudhani/fileboost/wiki)
227
+ - [Cloudflare Worker Setup Guide](https://github.com/bilalbudhani/fileboost-worker)
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require "bundler/setup"
2
+
3
+ APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
4
+ load "rails/tasks/engine.rake"
5
+
6
+ load "rails/tasks/statistics.rake"
7
+
8
+ require "bundler/gem_tasks"
@@ -0,0 +1,29 @@
1
+ module Fileboost
2
+ class Config
3
+ attr_accessor :project_id, :token
4
+
5
+ CDN_DOMAIN = "cdn.fileboost.dev"
6
+ BASE_URL = "https://#{CDN_DOMAIN}"
7
+
8
+ def initialize
9
+ @project_id = ENV["FILEBOOST_PROJECT_ID"]
10
+ @token = ENV["FILEBOOST_TOKEN"]
11
+ end
12
+
13
+ def valid?
14
+ project_id.present? && token.present?
15
+ end
16
+
17
+ def base_url
18
+ BASE_URL
19
+ end
20
+ end
21
+
22
+ def self.config
23
+ @config ||= Config.new
24
+ end
25
+
26
+ def self.configure
27
+ yield(config) if block_given?
28
+ end
29
+ end
@@ -0,0 +1,17 @@
1
+ module Fileboost
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace Fileboost
4
+
5
+ initializer "fileboost.action_view" do
6
+ ActiveSupport.on_load :action_view do
7
+ include Fileboost::Helpers
8
+ end
9
+ end
10
+
11
+ initializer "fileboost.active_storage" do
12
+ ActiveSupport.on_load :active_storage_blob do
13
+ # Extend ActiveStorage::Blob with fileboost-specific methods if needed
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,57 @@
1
+ module Fileboost
2
+ class ErrorHandler
3
+ class << self
4
+ def handle_with_fallback(error_context, &block)
5
+ begin
6
+ yield
7
+ rescue StandardError => e
8
+ log_error(error_context, e)
9
+
10
+ if Fileboost.config.fallback_to_rails
11
+ yield_fallback if block_given?
12
+ else
13
+ nil
14
+ end
15
+ end
16
+ end
17
+
18
+ def handle_gracefully(error_context, default_value = nil, &block)
19
+ begin
20
+ yield
21
+ rescue StandardError => e
22
+ log_error(error_context, e)
23
+ default_value
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def log_error(context, error)
30
+ return unless defined?(Rails) && Rails.logger
31
+
32
+ Rails.logger.warn(
33
+ "[Fileboost] Error in #{context}: #{error.class}: #{error.message}"
34
+ )
35
+
36
+ # Log backtrace in development for debugging
37
+ if Rails.env.development?
38
+ Rails.logger.debug(
39
+ "[Fileboost] Backtrace:\n#{error.backtrace.take(5).join("\n")}"
40
+ )
41
+ end
42
+ end
43
+
44
+ def yield_fallback
45
+ # This would be implemented by the calling code
46
+ # The pattern is to pass a fallback block when needed
47
+ nil
48
+ end
49
+ end
50
+ end
51
+
52
+ # Specific exception classes for better error handling
53
+ class ConfigurationError < StandardError; end
54
+ class SignatureGenerationError < StandardError; end
55
+ class UrlBuildError < StandardError; end
56
+ class AssetPathExtractionError < StandardError; end
57
+ end
@@ -0,0 +1,107 @@
1
+ require "active_storage"
2
+
3
+ module Fileboost
4
+ module Helpers
5
+ # Generate an optimized image tag using Fileboost
6
+ #
7
+ # @param asset [ActiveStorage::Blob, ActiveStorage::Attached, ActiveStorage::VariantWithRecord] The ActiveStorage image asset
8
+ # @param options [Hash] Image transformation and HTML options
9
+ # @return [String] HTML image tag
10
+ #
11
+ # Examples:
12
+ # fileboost_image_tag(user.avatar, resize: { w: 300, h: 200 }, alt: "Avatar")
13
+ # fileboost_image_tag(post.featured_image.blob, resize: { width: 1200, quality: 90 }, class: "hero-image")
14
+ def fileboost_image_tag(asset, **options)
15
+ # Extract resize options for transformation
16
+ resize_options = options.delete(:resize) || {}
17
+
18
+ # Generate the optimized URL
19
+ optimized_url = fileboost_url_for(asset, resize: resize_options)
20
+
21
+ # Return empty string if no URL could be generated
22
+ return "" if optimized_url.blank?
23
+
24
+ # Use the optimized URL with Rails image_tag for consistency
25
+ image_tag(optimized_url, **options)
26
+ end
27
+
28
+ # Generate an optimized URL using Fileboost
29
+ #
30
+ # @param asset [ActiveStorage::Blob, ActiveStorage::Attached, ActiveStorage::VariantWithRecord] The ActiveStorage image asset
31
+ # @param options [Hash] Image transformation options
32
+ # @return [String, nil] The optimized URL or nil if generation failed
33
+ #
34
+ # Examples:
35
+ # fileboost_url_for(post.image, resize: { width: 500, format: :webp })
36
+ # fileboost_url_for(user.avatar.blob, resize: { w: 1200, h: 400, q: 85 })
37
+ def fileboost_url_for(asset, **options)
38
+ # Validate that asset is an ActiveStorage object
39
+ unless valid_activestorage_asset?(asset)
40
+ Rails.logger.error("[Fileboost] Invalid asset type #{asset.class}. Only ActiveStorage objects are supported.") if defined?(Rails)
41
+ return nil
42
+ end
43
+
44
+ # Validate configuration
45
+ unless Fileboost.config.valid?
46
+ log_configuration_warning
47
+ return nil
48
+ end
49
+
50
+ # Build the optimized URL
51
+ Fileboost::UrlBuilder.build_url(asset, **options)
52
+ end
53
+
54
+ # Generate multiple image URLs for responsive images
55
+ #
56
+ # @param asset [ActiveStorage::Blob, ActiveStorage::Attached, ActiveStorage::VariantWithRecord] The ActiveStorage image asset
57
+ # @param sizes [Array<Hash>] Array of size configurations
58
+ # @param base_options [Hash] Base transformation options applied to all sizes
59
+ # @return [Hash] Hash with size keys and URL values
60
+ #
61
+ # Example:
62
+ # fileboost_responsive_urls(hero.image, [
63
+ # { width: 400, suffix: "sm" },
64
+ # { width: 800, suffix: "md" },
65
+ # { width: 1200, suffix: "lg" }
66
+ # ], resize: { quality: 85, format: :webp })
67
+ # # Returns: { "sm" => "url1", "md" => "url2", "lg" => "url3" }
68
+ def fileboost_responsive_urls(asset, sizes, **base_options)
69
+ urls = {}
70
+
71
+ sizes.each do |size_config|
72
+ suffix = size_config[:suffix] || size_config["suffix"]
73
+ size_options = size_config.except(:suffix, "suffix")
74
+ combined_options = base_options.merge(size_options)
75
+
76
+ url = fileboost_url_for(asset, **combined_options)
77
+ urls[suffix] = url if url.present?
78
+ end
79
+
80
+ urls
81
+ end
82
+
83
+ private
84
+
85
+
86
+ # Validate that the asset is a supported ActiveStorage object
87
+ def valid_activestorage_asset?(asset)
88
+ return true if asset.is_a?(ActiveStorage::Blob)
89
+ return true if asset.is_a?(ActiveStorage::Attached)
90
+ return true if asset.is_a?(ActiveStorage::VariantWithRecord)
91
+
92
+ false
93
+ end
94
+
95
+ # Log configuration warnings
96
+ def log_configuration_warning
97
+ missing_configs = []
98
+ missing_configs << "project_id" if Fileboost.config.project_id.blank?
99
+ missing_configs << "token" if Fileboost.config.token.blank?
100
+
101
+ Rails.logger.warn(
102
+ "[Fileboost] Configuration incomplete. Missing: #{missing_configs.join(', ')}. " \
103
+ "Set FILEBOOST_PROJECT_ID and FILEBOOST_TOKEN environment variables or configure them in your initializer."
104
+ ) if defined?(Rails)
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,44 @@
1
+ require "openssl"
2
+ require "base64"
3
+
4
+ module Fileboost
5
+ class SignatureGenerator
6
+ def self.generate(project_id:, asset_path:, params: {})
7
+ return nil unless project_id.present? && asset_path.present? && Fileboost.config.token.present?
8
+
9
+ # Sort parameters for consistent signature generation
10
+ sorted_params = params.sort.to_h
11
+ query_string = sorted_params.map { |k, v| "#{k}=#{v}" }.join("&")
12
+
13
+ # Create the signing string: project_id:asset_path:sorted_query_params
14
+ signing_string = [project_id, asset_path, query_string].join(":")
15
+ Rails.logger.debug("signature payload #{project_id}, #{asset_path}, #{query_string}, #{Fileboost.config.token}")
16
+ # Generate HMAC-SHA256 signature for secure authentication with Fileboost.dev
17
+ digest = OpenSSL::HMAC.digest("SHA256", Fileboost.config.token, signing_string)
18
+ # Use URL-safe base64 encoding and remove padding for maximum URL compatibility
19
+ Base64.urlsafe_encode64(digest, padding: false)
20
+ rescue StandardError => e
21
+ if defined?(Rails) && Rails.env.development?
22
+ raise e
23
+ else
24
+ Rails.logger.warn("[Fileboost] Failed to generate signature: #{e.message}") if defined?(Rails)
25
+ nil
26
+ end
27
+ end
28
+
29
+ def self.verify_signature(project_id:, asset_path:, params: {}, signature:)
30
+ expected_signature = generate(project_id: project_id, asset_path: asset_path, params: params)
31
+ return false if expected_signature.nil? || signature.nil?
32
+
33
+ # Use secure comparison to prevent timing attacks
34
+ ActiveSupport::SecurityUtils.secure_compare(signature, expected_signature)
35
+ rescue StandardError => e
36
+ if defined?(Rails) && Rails.env.development?
37
+ raise e
38
+ else
39
+ Rails.logger.warn("[Fileboost] Failed to verify signature: #{e.message}") if defined?(Rails)
40
+ false
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,155 @@
1
+ require "uri"
2
+
3
+ module Fileboost
4
+ class UrlBuilder
5
+ # Supported transformation parameters for Fileboost.dev service
6
+ TRANSFORMATION_PARAMS = %w[
7
+ w width
8
+ h height
9
+ q quality
10
+ f format
11
+ b blur
12
+ br brightness
13
+ c contrast
14
+ r rotation
15
+ fit
16
+ ].freeze
17
+
18
+ # Parameter aliases for convenience
19
+ PARAM_ALIASES = {
20
+ "width" => "w",
21
+ "height" => "h",
22
+ "quality" => "q",
23
+ "format" => "f",
24
+ "blur" => "b",
25
+ "brightness" => "br",
26
+ "contrast" => "c",
27
+ "rotation" => "r"
28
+ }.freeze
29
+
30
+ # Valid resize parameter keys
31
+ RESIZE_PARAMS = %w[w width h height q quality f format b blur br brightness c contrast r rotation fit].freeze
32
+
33
+ def self.build_url(asset, **options)
34
+ return nil unless Fileboost.config.valid?
35
+
36
+ asset_path = extract_asset_path(asset)
37
+ return nil unless asset_path.present?
38
+
39
+ project_id = Fileboost.config.project_id
40
+ base_url = Fileboost.config.base_url
41
+
42
+ # Build the full asset URL path for Fileboost.dev service
43
+ full_path = "/#{project_id}#{asset_path}"
44
+
45
+ # Extract and normalize transformation parameters
46
+ transformation_params = extract_transformation_params(options)
47
+
48
+ # Generate HMAC signature for secure authentication
49
+ signature = Fileboost::SignatureGenerator.generate(
50
+ project_id: project_id,
51
+ asset_path: asset_path,
52
+ params: transformation_params
53
+ )
54
+
55
+ return nil unless signature
56
+
57
+ # Add signature to parameters
58
+ all_params = transformation_params.merge("sig" => signature)
59
+
60
+ # Build final URL
61
+ uri = URI.join(base_url, full_path)
62
+ uri.query = URI.encode_www_form(all_params) unless all_params.empty?
63
+
64
+ uri.to_s
65
+ rescue StandardError => e
66
+ if defined?(Rails) && Rails.env.development?
67
+ raise e
68
+ else
69
+ Rails.logger.warn("[Fileboost] Failed to build URL: #{e.message}") if defined?(Rails)
70
+ nil
71
+ end
72
+ end
73
+
74
+ private
75
+
76
+ def self.extract_asset_path(asset)
77
+ case asset
78
+ when ActiveStorage::Blob
79
+ # ActiveStorage Blob
80
+ Rails.application.routes.url_helpers.rails_blob_path(asset, only_path: true)
81
+
82
+ when ActiveStorage::Attached
83
+ # ActiveStorage Attachment (has_one_attached, has_many_attached)
84
+ if asset.respond_to?(:blob) && asset.blob.present?
85
+ Rails.application.routes.url_helpers.rails_blob_path(asset.blob, only_path: true)
86
+ else
87
+ Rails.logger.warn("[Fileboost] ActiveStorage attachment has no blob") if defined?(Rails)
88
+ nil
89
+ end
90
+
91
+ when ActiveStorage::VariantWithRecord
92
+ # ActiveStorage Variant - use blob URL to avoid triggering variant generation
93
+ Rails.application.routes.url_helpers.rails_blob_path(asset.blob, only_path: true)
94
+
95
+ else
96
+ # Only ActiveStorage objects are supported
97
+ Rails.logger.warn("[Fileboost] Unsupported asset type: #{asset.class}. Only ActiveStorage objects are supported.") if defined?(Rails)
98
+ nil
99
+ end
100
+ rescue StandardError => e
101
+ if defined?(Rails) && Rails.env.development?
102
+ raise e
103
+ else
104
+ Rails.logger.warn("[Fileboost] Failed to extract asset path: #{e.message}") if defined?(Rails)
105
+ nil
106
+ end
107
+ end
108
+
109
+ def self.extract_transformation_params(options)
110
+ params = {}
111
+
112
+ # Only handle nested resize parameter
113
+ if options[:resize].is_a?(Hash)
114
+ resize_options = options[:resize]
115
+ resize_options.each do |key, value|
116
+ key_str = key.to_s
117
+
118
+ # Only process valid resize parameters
119
+ next unless RESIZE_PARAMS.include?(key_str)
120
+
121
+ # Use alias if available
122
+ param_key = PARAM_ALIASES[key_str] || key_str
123
+
124
+ # Convert value to string and validate
125
+ param_value = normalize_param_value(param_key, value)
126
+ next if param_value.blank?
127
+
128
+ params[param_key] = param_value
129
+ end
130
+ end
131
+
132
+ params
133
+ end
134
+
135
+ def self.normalize_param_value(key, value)
136
+ case key
137
+ when "w", "h", "q", "b", "br", "c", "r"
138
+ # Numeric parameters
139
+ value.to_i.to_s if value.to_i > 0
140
+ when "f"
141
+ # Format parameter - validate against common formats
142
+ valid_formats = %w[webp jpeg jpg png gif avif]
143
+ normalized = value.to_s.downcase
144
+ valid_formats.include?(normalized) ? normalized : nil
145
+ when "fit"
146
+ # Fit parameter - validate against supported values
147
+ valid_fits = %w[cover contain fill scale-down crop pad]
148
+ normalized = value.to_s.downcase.gsub("_", "-")
149
+ valid_fits.include?(normalized) ? normalized : nil
150
+ else
151
+ value.to_s
152
+ end
153
+ end
154
+ end
155
+ end
@@ -0,0 +1,3 @@
1
+ module Fileboost
2
+ VERSION = "0.1.0"
3
+ end
data/lib/fileboost.rb ADDED
@@ -0,0 +1,12 @@
1
+ require "fileboost/version"
2
+ require "fileboost/config"
3
+ require "fileboost/error_handler"
4
+ require "fileboost/signature_generator"
5
+ require "fileboost/url_builder"
6
+ require "fileboost/helpers"
7
+ require "fileboost/engine"
8
+
9
+ module Fileboost
10
+ # Fileboost provides seamless integration with the Fileboost.dev
11
+ # image optimization service for Rails applications
12
+ end
@@ -0,0 +1,27 @@
1
+ require "rails/generators/base"
2
+
3
+ module Fileboost
4
+ module Generators
5
+ class InstallGenerator < Rails::Generators::Base
6
+ desc "Create Fileboost initializer file"
7
+
8
+ def self.source_root
9
+ @source_root ||= File.expand_path("templates", __dir__)
10
+ end
11
+
12
+ def create_initializer_file
13
+ template "fileboost.rb", "config/initializers/fileboost.rb"
14
+ end
15
+
16
+ def show_readme
17
+ readme "INSTALL" if behavior == :invoke
18
+ end
19
+
20
+ private
21
+
22
+ def readme(path)
23
+ say File.read(File.join(self.class.source_root, "#{path}.md"))
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,41 @@
1
+ ===============================================================================
2
+
3
+ ⚡ Fileboost Installation Complete!
4
+
5
+ ===============================================================================
6
+
7
+ The Fileboost initializer has been created at:
8
+ config/initializers/fileboost.rb
9
+
10
+ Next steps:
11
+
12
+ 1. Set your environment variables:
13
+ export FILEBOOST_PROJECT_ID="your-project-id"
14
+ export FILEBOOST_TOKEN="your-secret-token"
15
+
16
+ 2. Or configure directly in the initializer file:
17
+ Edit config/initializers/fileboost.rb with your credentials
18
+
19
+ 3. Start using Fileboost helpers in your views:
20
+
21
+ <!-- Replace image_tag with fileboost_image_tag -->
22
+
23
+ <%= fileboost_image_tag user.avatar, alt: "Avatar", resize: {width: 100, height: 100, fit: "cover"} %>
24
+
25
+ <!-- Generate optimized URLs -->
26
+
27
+ <%= fileboost_url_for post.image %>
28
+
29
+ 4. Supported transformation options:
30
+ - width, height (or w, h)
31
+ - quality (or q): 1-100
32
+ - format (or f): webp, jpeg, png, gif, avif
33
+ - blur (or b): 0-100
34
+ - brightness (or br): 0-200
35
+ - contrast (or c): 0-200
36
+ - rotation (or r): 0-359
37
+ - fit: cover, contain, fill, scale-down, crop, pad
38
+
39
+ For more information, visit: https://fileboost.dev
40
+
41
+ ===============================================================================
@@ -0,0 +1,19 @@
1
+ # Fileboost Configuration
2
+ #
3
+ # Configure your Fileboost integration for seamless image optimization
4
+ # through the Fileboost.dev service. Set up your environment variables or
5
+ # configure the values directly below.
6
+ #
7
+ # Fileboost uses cdn.fileboost.dev as the CDN domain and only supports
8
+ # ActiveStorage objects.
9
+
10
+ Fileboost.configure do |config|
11
+ # Your unique Fileboost project identifier
12
+ # You can also set this via the FILEBOOST_PROJECT_ID environment variable
13
+ config.project_id = ENV["FILEBOOST_PROJECT_ID"] # || "your-project-id"
14
+
15
+ # HMAC signing secret for secure authentication with Fileboost.dev service
16
+ # You can also set this via the FILEBOOST_TOKEN environment variable
17
+ # IMPORTANT: Keep this secret secure and never commit it to version control
18
+ config.token = ENV["FILEBOOST_TOKEN"] # || "your-secret-token"
19
+ end
metadata ADDED
@@ -0,0 +1,129 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fileboost
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - bilal
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: activestorage
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '6.0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '6.0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: bundler
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '1.15'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '1.15'
40
+ - !ruby/object:Gem::Dependency
41
+ name: combustion
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '1.1'
47
+ type: :development
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '1.1'
54
+ - !ruby/object:Gem::Dependency
55
+ name: rake
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '13.0'
61
+ type: :development
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '13.0'
68
+ - !ruby/object:Gem::Dependency
69
+ name: rails
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: 8.0.0
75
+ type: :development
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: 8.0.0
82
+ description: Fileboost provides drop-in replacement Rails image helpers with automatic
83
+ optimization through the Fileboost.dev service. Works exclusively with ActiveStorage
84
+ objects, features HMAC authentication, and comprehensive transformation support.
85
+ email:
86
+ - bilal@bilalbudhani.com
87
+ executables: []
88
+ extensions: []
89
+ extra_rdoc_files: []
90
+ files:
91
+ - MIT-LICENSE
92
+ - README.md
93
+ - Rakefile
94
+ - lib/fileboost.rb
95
+ - lib/fileboost/config.rb
96
+ - lib/fileboost/engine.rb
97
+ - lib/fileboost/error_handler.rb
98
+ - lib/fileboost/helpers.rb
99
+ - lib/fileboost/signature_generator.rb
100
+ - lib/fileboost/url_builder.rb
101
+ - lib/fileboost/version.rb
102
+ - lib/generators/fileboost/install_generator.rb
103
+ - lib/generators/fileboost/templates/INSTALL.md
104
+ - lib/generators/fileboost/templates/fileboost.rb
105
+ homepage: https://github.com/bilalbudhani/fileboost-ruby
106
+ licenses:
107
+ - MIT
108
+ metadata:
109
+ homepage_uri: https://github.com/bilalbudhani/fileboost-ruby
110
+ source_code_uri: https://github.com/bilalbudhani/fileboost-ruby
111
+ changelog_uri: https://github.com/bilalbudhani/fileboost-ruby/blob/main/CHANGELOG.md
112
+ rdoc_options: []
113
+ require_paths:
114
+ - lib
115
+ required_ruby_version: !ruby/object:Gem::Requirement
116
+ requirements:
117
+ - - ">="
118
+ - !ruby/object:Gem::Version
119
+ version: '3.0'
120
+ required_rubygems_version: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ requirements: []
126
+ rubygems_version: 3.6.9
127
+ specification_version: 4
128
+ summary: Rails gem for Fileboost.dev image optimization with ActiveStorage
129
+ test_files: []