Skip to content

Commit 44e6bd4

Browse files
committed
Replace Minitest with RSpec, add specs/internal test app; update deps and refactor internals
- Add RSpec config, specs, and a lightweight internal Rails test app - Update gemspec/Gemfile.lock for bundler v2, rspec, combustion, Rails 8, sqlite3 - Add .rspec and Rake task to run specs - Refactor Fileboost internals: Config validation, Helpers, UrlBuilder, SignatureGenerator, error classes (raise errors, improve asset path/signature handling) - Remove legacy test/ (Minitest) files and unused engine initializer; update .gitignore
1 parent e476ea2 commit 44e6bd4

30 files changed

+546
-444
lines changed

.gitignore

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,5 @@
22
/doc/
33
/log/*.log
44
/pkg/
5-
/tmp/
6-
/test/dummy/db/*.sqlite3
7-
/test/dummy/db/*.sqlite3-*
8-
/test/dummy/log/*.log
9-
/test/dummy/storage/
10-
/test/dummy/tmp/
5+
/spec/internal/tmp/
116
Claude.md

.rspec

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
--color
2+
--require spec_helper
3+
--format documentation

Gemfile.lock

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
PATH
22
remote: .
33
specs:
4-
fileboost (0.1.0)
5-
rails (>= 8.0.2.1)
4+
fileboost (0.1.1)
5+
activestorage (>= 6.0)
66

77
GEM
88
remote: https://rubygems.org/
@@ -83,10 +83,15 @@ GEM
8383
benchmark (0.4.1)
8484
bigdecimal (3.2.3)
8585
builder (3.3.0)
86+
combustion (1.5.0)
87+
activesupport (>= 3.0.0)
88+
railties (>= 3.0.0)
89+
thor (>= 0.14.6)
8690
concurrent-ruby (1.3.5)
8791
connection_pool (2.5.4)
8892
crass (1.0.6)
8993
date (3.4.1)
94+
diff-lcs (1.6.2)
9095
drb (2.2.3)
9196
erb (5.0.2)
9297
erubi (1.13.1)
@@ -189,6 +194,27 @@ GEM
189194
regexp_parser (2.11.2)
190195
reline (0.6.2)
191196
io-console (~> 0.5)
197+
rspec (3.13.1)
198+
rspec-core (~> 3.13.0)
199+
rspec-expectations (~> 3.13.0)
200+
rspec-mocks (~> 3.13.0)
201+
rspec-core (3.13.5)
202+
rspec-support (~> 3.13.0)
203+
rspec-expectations (3.13.5)
204+
diff-lcs (>= 1.2.0, < 2.0)
205+
rspec-support (~> 3.13.0)
206+
rspec-mocks (3.13.5)
207+
diff-lcs (>= 1.2.0, < 2.0)
208+
rspec-support (~> 3.13.0)
209+
rspec-rails (6.1.5)
210+
actionpack (>= 6.1)
211+
activesupport (>= 6.1)
212+
railties (>= 6.1)
213+
rspec-core (~> 3.13)
214+
rspec-expectations (~> 3.13)
215+
rspec-mocks (~> 3.13)
216+
rspec-support (~> 3.13)
217+
rspec-support (3.13.5)
192218
rubocop (1.80.2)
193219
json (~> 2.3)
194220
language_server-protocol (~> 3.17.0.2)
@@ -240,11 +266,17 @@ PLATFORMS
240266
arm64-darwin
241267

242268
DEPENDENCIES
269+
bundler (~> 2)
270+
combustion (~> 1)
243271
fileboost!
244272
propshaft
245273
puma
274+
rails (~> 8.0.0)
275+
rake (~> 13.0)
276+
rspec (~> 3.0)
277+
rspec-rails (~> 6.0)
246278
rubocop-rails-omakase
247-
sqlite3
279+
sqlite3 (~> 2.0, >= 0)
248280

249281
BUNDLED WITH
250282
2.7.1

Rakefile

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
require "bundler/setup"
2+
require "bundler/gem_tasks"
3+
require "rspec/core/rake_task"
24

3-
APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
4-
load "rails/tasks/engine.rake"
5-
6-
load "rails/tasks/statistics.rake"
5+
RSpec::Core::RakeTask.new(:spec)
76

8-
require "bundler/gem_tasks"
7+
task default: :spec

fileboost.gemspec

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,11 @@ Gem::Specification.new do |spec|
2222

2323
spec.add_runtime_dependency "activestorage", ">= 6.0"
2424

25-
spec.add_development_dependency "bundler", "~> 1.15"
26-
spec.add_development_dependency "combustion", "~> 1.1"
25+
spec.add_development_dependency "bundler", "~> 2"
26+
spec.add_development_dependency "combustion", "~> 1"
2727
spec.add_development_dependency "rake", "~> 13.0"
2828
spec.add_development_dependency "rails", "~> 8.0.0"
29+
spec.add_development_dependency "rspec", "~> 3.0"
30+
spec.add_development_dependency "rspec-rails", "~> 6.0"
31+
spec.add_development_dependency "sqlite3", "~> 2.0"
2932
end

lib/fileboost/config.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ def initialize
1111
end
1212

1313
def valid?
14-
project_id.present? && token.present?
14+
!project_id.empty? && !token.empty?
1515
end
1616

1717
def base_url
@@ -26,4 +26,4 @@ def self.config
2626
def self.configure
2727
yield(config) if block_given?
2828
end
29-
end
29+
end

lib/fileboost/engine.rb

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,5 @@ class Engine < ::Rails::Engine
77
include Fileboost::Helpers
88
end
99
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
1610
end
1711
end

lib/fileboost/error_handler.rb

Lines changed: 1 addition & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,7 @@
11
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-
522
# Specific exception classes for better error handling
533
class ConfigurationError < StandardError; end
544
class SignatureGenerationError < StandardError; end
555
class UrlBuildError < StandardError; end
566
class AssetPathExtractionError < StandardError; end
57-
end
7+
end

lib/fileboost/helpers.rb

Lines changed: 7 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,6 @@ def fileboost_image_tag(asset, **options)
1818
# Generate the optimized URL
1919
optimized_url = fileboost_url_for(asset, resize: resize_options)
2020

21-
# Return empty string if no URL could be generated
22-
return "" if optimized_url.blank?
23-
2421
# Use the optimized URL with Rails image_tag for consistency
2522
image_tag(optimized_url, **options)
2623
end
@@ -36,16 +33,10 @@ def fileboost_image_tag(asset, **options)
3633
# fileboost_url_for(user.avatar.blob, resize: { w: 1200, h: 400, q: 85 })
3734
def fileboost_url_for(asset, **options)
3835
# 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
36+
raise ArgumentError, "Invalid asset type #{asset.class}. Only ActiveStorage objects are supported." unless valid_activestorage_asset?(asset)
4337

4438
# Validate configuration
45-
unless Fileboost.config.valid?
46-
log_configuration_warning
47-
return nil
48-
end
39+
raise Fileboost::ConfigurationError, "Invalid Fileboost configuration" unless Fileboost.config.valid?
4940

5041
# Build the optimized URL
5142
Fileboost::UrlBuilder.build_url(asset, **options)
@@ -71,10 +62,13 @@ def fileboost_responsive_urls(asset, sizes, **base_options)
7162
sizes.each do |size_config|
7263
suffix = size_config[:suffix] || size_config["suffix"]
7364
size_options = size_config.except(:suffix, "suffix")
74-
combined_options = base_options.merge(size_options)
65+
66+
# Merge size options into base resize options
67+
merged_resize_options = (base_options[:resize] || {}).merge(size_options)
68+
combined_options = base_options.merge(resize: merged_resize_options)
7569

7670
url = fileboost_url_for(asset, **combined_options)
77-
urls[suffix] = url if url.present?
71+
urls[suffix] = url if !url.nil? && !url.empty?
7872
end
7973

8074
urls
@@ -91,17 +85,5 @@ def valid_activestorage_asset?(asset)
9185

9286
false
9387
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
10688
end
10789
end

lib/fileboost/signature_generator.rb

Lines changed: 2 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3,42 +3,17 @@
33

44
module Fileboost
55
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-
6+
def self.generate(asset_path:, params: {})
97
# Sort parameters for consistent signature generation
108
sorted_params = params.sort.to_h
119
query_string = sorted_params.map { |k, v| "#{k}=#{v}" }.join("&")
1210

1311
# 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}")
12+
signing_string = [ Fileboost.config.project_id, asset_path, query_string ].join(":")
1613
# Generate HMAC-SHA256 signature for secure authentication with Fileboost.dev
1714
digest = OpenSSL::HMAC.digest("SHA256", Fileboost.config.token, signing_string)
1815
# Use URL-safe base64 encoding and remove padding for maximum URL compatibility
1916
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
4217
end
4318
end
4419
end

0 commit comments

Comments
 (0)