Skip to content

Commit

Permalink
Get custom fields traits from configuration instead of FactoryBot
Browse files Browse the repository at this point in the history
Refactored some methods to use `OpenProject::CustomFieldFormat` to get
the `multi_value_possible` information.
  • Loading branch information
cbliard committed Jan 22, 2025
1 parent fc310c3 commit 8bb942d
Show file tree
Hide file tree
Showing 6 changed files with 88 additions and 24 deletions.
2 changes: 0 additions & 2 deletions app/helpers/custom_fields_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -205,8 +205,6 @@ def format_value(value, custom_field)
# Return an array of custom field formats which can be used in select_tag
def custom_field_formats_for_select(custom_field)
OpenProject::CustomFieldFormat.all_for_field(custom_field)
.sort_by(&:order)
.reject { |format| format.label.nil? }
.map do |custom_field_format|
[label_for_custom_field_format(custom_field_format.name), custom_field_format.name]
end
Expand Down
2 changes: 1 addition & 1 deletion app/models/custom_field.rb
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ def field_format_hierarchy?
end

def multi_value_possible?
version? || user? || list? || field_format_hierarchy?
OpenProject::CustomFieldFormat.find_by(name: field_format)&.multi_value_possible?
end

def allow_non_open_versions_possible?
Expand Down
4 changes: 4 additions & 0 deletions config/initializers/custom_field_format.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
fields.register OpenProject::CustomFieldFormat.new("list",
label: :label_list,
order: 6,
multi_value_possible: true,
formatter: "CustomValue::ListStrategy")
fields.register OpenProject::CustomFieldFormat.new("date",
label: :label_date,
Expand All @@ -64,12 +65,14 @@
only: %w(WorkPackage TimeEntry Version Project),
edit_as: "list",
order: 9,
multi_value_possible: true,
formatter: "CustomValue::UserStrategy")
fields.register OpenProject::CustomFieldFormat.new("version",
label: Proc.new { Version.model_name.human },
only: %w(WorkPackage TimeEntry Version Project),
edit_as: "list",
order: 10,
multi_value_possible: true,
formatter: "CustomValue::VersionStrategy")
# This is an internal formatter used as a fallback in case a value is not found.
# Setting the label to nil in order to avoid it becoming available for selection as a custom value format.
Expand All @@ -82,5 +85,6 @@
label: :label_hierarchy,
only: %w(WorkPackage),
order: 12,
multi_value_possible: true,
formatter: "CustomValue::HierarchyStrategy")
end
36 changes: 26 additions & 10 deletions lib/open_project/custom_field_format.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,29 @@ module OpenProject
class CustomFieldFormat
include Redmine::I18n

cattr_accessor :available
cattr_reader :available
@@available = {}

attr_accessor :name, :order, :label, :edit_as, :class_names, :formatter
attr_reader :name, :order, :label, :edit_as, :class_names

def initialize(name, label:, order:, edit_as: name, only: nil, formatter: "CustomValue::StringStrategy")
self.name = name
self.label = label
self.order = order
self.edit_as = edit_as
self.class_names = only
self.formatter = formatter
def initialize(name,
label:,
order:,
edit_as: name,
only: nil,
multi_value_possible: false,
formatter: "CustomValue::StringStrategy")
@name = name
@label = label
@order = order
@edit_as = edit_as
@class_names = only
@multi_value_possible = multi_value_possible
@formatter = formatter
end

def multi_value_possible?
@multi_value_possible
end

def formatter
Expand All @@ -56,7 +67,7 @@ def map(&)

# Registers a custom field format
def register(custom_field_format, _options = {})
@@available[custom_field_format.name] = custom_field_format unless @@available.keys.include?(custom_field_format.name)
@@available[custom_field_format.name] = custom_field_format unless @@available.include?(custom_field_format.name)
end

def available_formats
Expand All @@ -69,10 +80,15 @@ def find_by(name:)

def all_for_field(custom_field)
class_name = custom_field.class.customized_class.name
all_for_class_name(class_name)
end

def all_for_class_name(class_name)
available
.values
.select { |field| field.class_names.nil? || field.class_names.include?(class_name) }
.sort_by(&:order)
.reject { |format| format.label.nil? }
end
end
end
Expand Down
17 changes: 6 additions & 11 deletions spec/features/work_packages/tabs/relations_children_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
require "support/edit_fields/edit_field"

RSpec.describe "Relations children tab", :js, :with_cuprite do
include CustomFieldsHelpers

shared_let(:normal_cf) { create(:string_wp_custom_field, is_required: false) }
shared_let(:required_cf) { create(:string_wp_custom_field, is_required: true) }
shared_let(:type_milestone) { create(:type_milestone) }
Expand Down Expand Up @@ -118,20 +120,13 @@
let!(:user) { create(:admin) }

before do
# Introspect FactoryBot to find all traits used to create work package custom fields
traits = FactoryBot.factories[:wp_custom_field].defined_traits
traits = traits.reject { |t| t.name == "multi_value" }
traits = traits.map { |t| t.name.to_sym }

traits.each do |trait|
[true, false].each do |required| # rubocop:disable Performance/CollectionLiteralInLoop
cf = create(:wp_custom_field,
trait,
is_required: required)
factory_bot_custom_field_traits_for("WorkPackage")
.product([true, false])
.each do |trait, is_required|
cf = create(:wp_custom_field, trait, is_required:)
project.types.first.custom_fields << cf
project.work_package_custom_fields << cf
end
end
end

it "displays a field for each required custom field" do
Expand Down
51 changes: 51 additions & 0 deletions spec/support/custom_fields_helpers.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# frozen_string_literal: true

#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++

module CustomFieldsHelpers
def factory_bot_custom_field_traits_for(class_name)
OpenProject::CustomFieldFormat
.all_for_class_name(class_name)
.flat_map do |format|
trait_name = trait_name(format.name)
[
trait_name,
format.multi_value_possible? ? "multi_#{trait_name}" : nil
].compact
end
end

def trait_name(custom_field_format_name)
case custom_field_format_name
when "int" then "integer"
when "bool" then "boolean"
else custom_field_format_name
end
end
end

0 comments on commit 8bb942d

Please sign in to comment.