Skip to content

Commit

Permalink
Merge pull request #17439 from opf/impl/subject-type-pattern
Browse files Browse the repository at this point in the history
Adds support of storing patterns on Type
  • Loading branch information
mereghost authored Dec 16, 2024
2 parents 81b7579 + 3629d5d commit ded8230
Show file tree
Hide file tree
Showing 7 changed files with 274 additions and 12 deletions.
16 changes: 5 additions & 11 deletions app/models/type.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ class Type < ApplicationRecord

include ::Scopes::Scoped

attribute :patterns, Types::PatternCollectionType.new

before_destroy :check_integrity

has_many :work_packages
Expand All @@ -50,27 +52,19 @@ def copy_from_type(source_type)
join_table: "#{table_name_prefix}custom_fields_types#{table_name_suffix}",
association_foreign_key: "custom_field_id"

belongs_to :color,
optional: true,
class_name: "Color"
belongs_to :color, optional: true, class_name: "Color"

acts_as_list

validates :name,
presence: true,
uniqueness: { case_sensitive: false },
length: { maximum: 255 }
validates :name, presence: true, uniqueness: { case_sensitive: false }, length: { maximum: 255 }

validates :is_default, :is_milestone, inclusion: { in: [true, false] }

scopes :milestone

default_scope { order("position ASC") }

scope :without_standard, -> {
where(is_standard: false)
.order(:position)
}
scope :without_standard, -> { where(is_standard: false).order(:position) }

def to_s; name end

Expand Down
42 changes: 42 additions & 0 deletions app/models/types/pattern.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# 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 Types
Pattern = Data.define(:blueprint, :enabled) do
def call(object)
# calculate string using object
blueprint.to_s + object.to_s
end

def to_h
super.stringify_keys
end
end
end
53 changes: 53 additions & 0 deletions app/models/types/pattern_collection.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# 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 Types
PatternCollection = Data.define(:patterns) do
private_class_method :new

def self.build(patterns:, contract: PatternCollectionContract.new)
contract.call(patterns).to_monad.fmap { |success| new(success.to_h) }
end

def initialize(patterns:)
transformed = patterns.transform_values { Pattern.new(**_1) }.freeze

super(patterns: transformed)
end

def [](value)
patterns.fetch(value)
end

def to_h
patterns.stringify_keys.transform_values(&:to_h)
end
end
end
40 changes: 40 additions & 0 deletions app/models/types/pattern_collection_contract.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# 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 Types
class PatternCollectionContract < Dry::Validation::Contract
params do
required(:subject).hash do
required(:blueprint).filled(:string)
required(:enabled).filled(:bool)
end
end
end
end
54 changes: 54 additions & 0 deletions app/models/types/pattern_collection_type.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# 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 Types
class PatternCollectionType < ActiveModel::Type::Value
def assert_valid_value(value)
cast(value)
end

def cast(value)
PatternCollection.build(patterns: value).value_or { nil }
end

def serialize(pattern)
return super if pattern.nil?

YAML.dump(pattern.to_h)
end

def deserialize(value)
return if value.blank?

data = YAML.safe_load(value)
cast(data)
end
end
end
37 changes: 37 additions & 0 deletions db/migrate/20241211152749_introduce_patterns_to_types.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# 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.
#++

class IntroducePatternsToTypes < ActiveRecord::Migration[7.1]
def change
change_table :types do |t|
t.text :patterns, null: true
end
end
end
44 changes: 43 additions & 1 deletion spec/models/type_spec.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# frozen_string_literal: true

#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
Expand Down Expand Up @@ -42,7 +44,7 @@
end

it "returns the types enabled in the provided project" do
expect(Type.enabled_in(project)).to contain_exactly(type)
expect(described_class.enabled_in(project)).to contain_exactly(type)
end
end

Expand Down Expand Up @@ -125,4 +127,44 @@
end
end
end

describe "#patterns" do
it "returns nil when no patterns are defined" do
type = create(:type)

expect(type.patterns).to be_nil
end

it "returns a PatternCollection" do
type = create(:type, patterns: {
subject: { blueprint: "{{work_package:custom_field_123}} - {{project:custom_field_321}}", enabled: true }
})

expect(type.patterns).to be_a(Types::PatternCollection)
expect(type.patterns[:subject])
.to eq(Types::Pattern.new("{{work_package:custom_field_123}} - {{project:custom_field_321}}", true))
end
end

describe "#patterns=" do
subject(:type) { build(:type) }

context "when an invalid value is passed" do
it "defaults to nil" do
type.patterns = 4

expect(type.patterns).to be_nil
expect { type.save! }.not_to raise_error
end
end

it "converts the incoming hash into a PatternCollection" do
type.patterns = { subject: { blueprint: "some_string", enabled: false } }

expect(type.patterns).to be_a(Types::PatternCollection)
expect(type.patterns[:subject]).to be_a(Types::Pattern)

expect { type.save! }.not_to raise_error
end
end
end

0 comments on commit ded8230

Please sign in to comment.