Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/active_storage_validations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,6 @@
require "active_storage_validations/total_size_validator"
require "active_storage_validations/pages_validator"

require "active_storage_validations/form_builder"
require "active_storage_validations/engine"
require "active_storage_validations/railtie"
39 changes: 39 additions & 0 deletions lib/active_storage_validations/form_builder.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# frozen_string_literal: true

module ActiveStorageValidations
module FormBuilder
def file_field(method, options = {})
if options[:accept].blank?
accept = inferred_accept_types(method)
options[:accept] = accept.join(",") if accept.any?
end

super(method, options)
end

private

def inferred_accept_types(method)
return [] unless @object.class.respond_to?(:validators_on)

content_type_validators = @object.class.validators_on(method).select do |v|
v.is_a?(ActiveStorageValidations::ContentTypeValidator)
end

content_type_validators.flat_map do |validator|
types = Array(validator.options[:with]) + Array(validator.options[:in])
types.filter_map do |type|
case type
when String
type.include?("/") ? type : Marcel::MimeType.for(declared_type: type, extension: type)
when Symbol
Marcel::MimeType.for(declared_type: type.to_s, extension: type.to_s)
when Regexp
match = type.source.match(%r{\A\\A([a-z]+)/\.\*\\z\z})
"#{match[1]}/*" if match
end
Copy link
Copy Markdown
Collaborator

@Mth0158 Mth0158 Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For Regex we could make it a bit more practical.
Let's say the dev has set up exactly content_type: /\Aimage\/.*\z/, we could translate it to accept="image/*". We could do this for all content_type categories (image, video, application, ...). Other regexes would be ignored as previously committed.
What do you think?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pardeyke thanks for the addition, could you update the tests accordingly? If so we can merge it soon! :)

end
end.uniq
end
end
end
6 changes: 6 additions & 0 deletions lib/active_storage_validations/railtie.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ class Railtie < ::Rails::Railtie
end
end

initializer "active_storage_validations.form_builder" do
ActiveSupport.on_load(:action_view) do
ActionView::Helpers::FormBuilder.prepend(ActiveStorageValidations::FormBuilder)
end
end

initializer "active_storage_validations.extend_active_storage_blob" do
ActiveSupport.on_load(:active_storage_blob) do
include ActiveStorageValidations::ASVBlobMetadatable
Expand Down
7 changes: 7 additions & 0 deletions test/dummy/app/models/form_builder.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# frozen_string_literal: true

module FormBuilder
def self.table_name_prefix
"form_builder_"
end
end
35 changes: 35 additions & 0 deletions test/dummy/app/models/form_builder/check.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# frozen_string_literal: true

# == Schema Information
#
# Table name: form_builder_checks
#
# id :integer not null, primary key
# created_at :datetime not null
# updated_at :datetime not null
#

class FormBuilder::Check < ApplicationRecord
has_one_attached :with_symbol
validates :with_symbol, content_type: :png

has_one_attached :in_array
validates :in_array, content_type: [ :png, :gif ]

has_one_attached :with_string_mime
validates :with_string_mime, content_type: "image/png"

has_one_attached :with_proc
validates :with_proc, content_type: ->(record) { :png }

has_one_attached :with_regex
validates :with_regex, content_type: /\Aimage\/.*\z/

has_one_attached :with_non_matching_regex
validates :with_non_matching_regex, content_type: /\Aimage\/(png|gif)\z/

has_one_attached :no_content_type_validator
validates :no_content_type_validator, attached: true

has_one_attached :no_validator
end
5 changes: 5 additions & 0 deletions test/dummy/db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,11 @@
t.datetime :updated_at, precision: 6, null: false
end

create_table :form_builder_checks, force: :cascade do |t|
t.datetime :created_at, null: false
t.datetime :updated_at, null: false
end

create_table :users, force: :cascade do |t|
t.string :name
t.datetime :created_at, null: false
Expand Down
57 changes: 57 additions & 0 deletions test/form_builder/form_builder_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# frozen_string_literal: true

require "test_helper"

describe ActiveStorageValidations::FormBuilder do
let(:model) { FormBuilder::Check.new }
let(:builder) { ActionView::Helpers::FormBuilder.new(:check, model, ActionView::Base.empty, {}) }

describe "file_field with content_type validator" do
it "auto-sets accept from :with symbol option" do
html = builder.file_field(:with_symbol)
assert_includes html, 'accept="image/png"'
end

it "auto-sets accept from :in array option" do
html = builder.file_field(:in_array)
assert_includes html, "image/png"
assert_includes html, "image/gif"
end

it "auto-sets accept from string MIME type" do
html = builder.file_field(:with_string_mime)
assert_includes html, 'accept="image/png"'
end

it "does not override explicit accept option" do
html = builder.file_field(:with_symbol, accept: "image/jpeg")
assert_includes html, 'accept="image/jpeg"'
refute_includes html, "image/png"
end

it "handles models without content_type validators" do
html = builder.file_field(:no_content_type_validator)
refute_includes html, "accept="
end

it "handles attachments with no validators at all" do
html = builder.file_field(:no_validator)
refute_includes html, "accept="
end

it "skips Proc options gracefully" do
html = builder.file_field(:with_proc)
refute_includes html, "accept="
end

it "auto-sets accept from Regexp content type" do
html = builder.file_field(:with_regex)
assert_includes html, 'accept="image/*"'
end

it "skips non-matching Regexp options gracefully" do
html = builder.file_field(:with_non_matching_regex)
refute_includes html, "accept="
end
end
end