Vendor Foundation form builder to remove gem dependency

The "foundation_rails_helper" gem is no longer maintained and is
incompatible with Rails 7.1. To avoid blocking the upgrade, we've vendored
the vendor/foundation_rails_helper/form_builder.rb as a copy of the
original FormBuilder class.

To mantain compatibility with auto_labels and button_class variables, that
are used in the original builder, we are overwriting in the foundation
form builder initializer.

The gem has been removed from the Gemfile and replaced with this vendored
fallback. This workaround is safe to remove once legacy Foundation CSS
support is dropped.

All vendored code retains the original MIT license and attribution.
This commit is contained in:
taitus
2025-04-14 14:36:35 +02:00
parent e93d303b1e
commit e662c704ac
5 changed files with 283 additions and 7 deletions

View File

@@ -25,7 +25,6 @@ gem "devise-security", "~> 0.18.0"
gem "exiftool_vendored", "~> 12.97.0"
gem "file_validators", "~> 3.0.0"
gem "font-awesome-sass", "~> 5.15.1" # Remember to update vendor/assets/images/fontawesome when updating this gem
gem "foundation_rails_helper", "~> 4.0.1"
gem "globalize", "~> 7.0.0"
gem "globalize-accessors", "~> 0.3.0"
gem "graphiql-rails", "~> 1.8.0"

View File

@@ -239,11 +239,6 @@ GEM
mime-types (>= 1.0)
font-awesome-sass (5.15.1)
sassc (>= 1.11)
foundation_rails_helper (4.0.1)
actionpack (>= 4.1, < 7.1)
activemodel (>= 4.1, < 7.1)
activesupport (>= 4.1, < 7.1)
railties (>= 4.1, < 7.1)
gitlab (4.20.1)
httparty (~> 0.20)
terminal-table (>= 1.5.1)
@@ -753,7 +748,6 @@ DEPENDENCIES
faraday-retry (~> 2.2.1)
file_validators (~> 3.0.0)
font-awesome-sass (~> 5.15.1)
foundation_rails_helper (~> 4.0.1)
globalize (~> 7.0.0)
globalize-accessors (~> 0.3.0)
graphiql-rails (~> 1.8.0)

View File

@@ -0,0 +1,16 @@
require Rails.root.join("vendor/foundation_rails_helper/form_builder.rb")
class FoundationRailsHelper::FormBuilder
def column_classes(...)
""
end
def auto_labels
true
end
def submit(value = nil, options = {})
options[:class] ||= "success button"
super
end
end

22
vendor/foundation_rails_helper/LICENSE vendored Normal file
View File

@@ -0,0 +1,22 @@
Copyright (c) 2015 Sébastien Gruhier
MIT License
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,245 @@
# frozen_string_literal: true
# Copyright (c) 2015 Sébastien Gruhier (http://xilinus.com/)
# MIT License
# Vendored copy from https://github.com/sgruhier/foundation_rails_helper/blob/master/lib/foundation_rails_helper/form_builder.rb
#
# This file was vendored to work around the Rails 7.1 incompatibility,
# since the `foundation_rails_helper` gem is no longer maintained.
#
# See LICENSE in vendor/foundation_rails_helper/LICENSE
require 'action_view/helpers'
module FoundationRailsHelper
class FormBuilder < ActionView::Helpers::FormBuilder
include ActionView::Helpers::TagHelper
include ActionView::Helpers::OutputSafetyHelper
%w(file_field email_field text_field text_area telephone_field phone_field
url_field number_field date_field datetime_field datetime_local_field
month_field week_field time_field range_field search_field color_field)
.each do |method_name|
define_method(method_name) do |*args|
attribute = args[0]
options = args[1] || {}
field(attribute, options) do |opts|
super(attribute, opts)
end
end
end
def label(attribute, text = nil, options = {})
if error?(attribute)
options[:class] ||= ''
options[:class] += ' is-invalid-label'
end
super(attribute, (text || '').html_safe, options)
end
# rubocop:disable LineLength
def check_box(attribute, options = {}, checked_value = '1', unchecked_value = '0')
custom_label(attribute, options[:label], options[:label_options]) do
options.delete(:label)
options.delete(:label_options)
super(attribute, options, checked_value, unchecked_value)
end + error_and_help_text(attribute, options)
end
# rubocop:enable LineLength
def radio_button(attribute, tag_value, options = {})
options[:label_options] ||= {}
label_options = options.delete(:label_options).merge!(value: tag_value)
label_text = options.delete(:label)
l = label(attribute, label_text, label_options) unless label_text == false
r = @template.radio_button(@object_name, attribute, tag_value,
objectify_options(options))
"#{r}#{l}".html_safe
end
def password_field(attribute, options = {})
field attribute, options do |opts|
super(attribute, opts.merge(autocomplete: :off))
end
end
def datetime_select(attribute, options = {}, html_options = {})
field attribute, options, html_options do |html_opts|
super(attribute, options, html_opts.merge(autocomplete: :off))
end
end
def date_select(attribute, options = {}, html_options = {})
field attribute, options, html_options do |html_opts|
super(attribute, options, html_opts.merge(autocomplete: :off))
end
end
# rubocop:disable LineLength
def time_zone_select(attribute, priorities = nil, options = {}, html_options = {})
field attribute, options, html_options do |html_opts|
super(attribute, priorities, options,
html_opts.merge(autocomplete: :off))
end
end
# rubocop:enable LineLength
def select(attribute, choices, options = {}, html_options = {})
field attribute, options, html_options do |html_opts|
html_options[:autocomplete] ||= :off
super(attribute, choices, options, html_opts)
end
end
# rubocop:disable LineLength, ParameterLists
def collection_select(attribute, collection, value_method, text_method, options = {}, html_options = {})
field attribute, options, html_options do |html_opts|
html_options[:autocomplete] ||= :off
super(attribute, collection, value_method, text_method, options,
html_opts)
end
end
def grouped_collection_select(attribute, collection, group_method, group_label_method, option_key_method, option_value_method, options = {}, html_options = {})
field attribute, options, html_options do |html_opts|
html_options[:autocomplete] ||= :off
super(attribute, collection, group_method, group_label_method,
option_key_method, option_value_method, options, html_opts)
end
end
# rubocop:enable LineLength, ParameterLists
def autocomplete(attribute, url, options = {})
field attribute, options do |opts|
opts.merge!(update_elements: opts[:update_elements],
min_length: 0, value: object.send(attribute))
autocomplete_field(attribute, url, opts)
end
end
def submit(value = nil, options = {})
options[:class] ||= FoundationRailsHelper.configuration.button_class
super(value, options)
end
def error_for(attribute, options = {})
return unless error?(attribute)
class_name = 'form-error is-visible'
class_name += " #{options[:class]}" if options[:class]
error_messages = object.errors[attribute].join(', ')
error_messages = error_messages.html_safe if options[:html_safe_errors]
content_tag(:small, error_messages,
class: class_name.sub('is-invalid-input', ''))
end
private
def error?(attribute)
object.respond_to?(:errors) && !object.errors[attribute].blank?
end
def default_label_text(object, attribute)
if object.class.respond_to?(:human_attribute_name)
return object.class.human_attribute_name(attribute)
end
attribute.to_s.humanize
end
def custom_label(attribute, text, options)
return block_given? ? yield.html_safe : ''.html_safe if text == false
text = default_label_text(object, attribute) if text.nil? || text == true
text = safe_join([yield, text.html_safe]) if block_given?
label(attribute, text, options || {})
end
def column_classes(options)
classes = SizeClassCalculator.new(options).classes
classes + ' columns'
end
def tag_from_options(name, options)
return ''.html_safe unless options && options[:value].present?
content_tag(:div,
content_tag(:span, options[:value], class: name),
class: column_classes(options).to_s)
end
def decrement_input_size(input, column, options)
return unless options.present? && options.key?(column)
input.send("#{column}=",
(input.send(column) - options.fetch(column).to_i))
input.send('changed?=', true)
end
def calculate_input_size(prefix_options, postfix_options)
input_size =
OpenStruct.new(changed?: false, small: 12, medium: 12, large: 12)
%w(small medium large).each do |size|
decrement_input_size(input_size, size.to_sym, prefix_options)
decrement_input_size(input_size, size.to_sym, postfix_options)
end
input_size
end
def wrap_prefix_and_postfix(block, prefix_options, postfix_options)
prefix = tag_from_options('prefix', prefix_options)
postfix = tag_from_options('postfix', postfix_options)
input_size = calculate_input_size(prefix_options, postfix_options)
klass = column_classes(input_size.marshal_dump).to_s
input = content_tag(:div, block, class: klass)
return block unless input_size.changed?
content_tag(:div, prefix + input + postfix, class: 'row collapse')
end
def error_and_help_text(attribute, options = {})
html = ''
if options[:help_text]
html += content_tag(:p, options[:help_text], class: 'help-text')
end
html += error_for(attribute, options) || ''
html.html_safe
end
def field_label(attribute, options)
return ''.html_safe unless auto_labels || options[:label]
custom_label(attribute, options[:label], options[:label_options])
end
def field(attribute, options, html_options = nil)
html = field_label(attribute, options)
class_options = html_options || options
if error?(attribute)
class_options[:class] = class_options[:class].to_s
class_options[:class] += ' is-invalid-input'
end
options.delete(:label)
options.delete(:label_options)
help_text = options.delete(:help_text)
prefix = options.delete(:prefix)
postfix = options.delete(:postfix)
html += wrap_prefix_and_postfix(yield(class_options), prefix, postfix)
html + error_and_help_text(attribute, options.merge(help_text: help_text))
end
def auto_labels
if @options[:auto_labels].nil?
FoundationRailsHelper.configuration.auto_labels
else
@options[:auto_labels]
end
end
end
end