From 0a3de6820640f628ed2284445a5d8b9d979278da Mon Sep 17 00:00:00 2001 From: taitus Date: Sat, 23 Jan 2021 13:45:36 +0100 Subject: [PATCH] Add relation between Goal and LocalTarget This is similar to what we do with investments, which belong to a heading but also belong to a budget. In our case, the reason is we've been asked to add local targets which belong to a goal but are not related to any existing target. Even though we're not implementing that case right now, we're adding the relation so we don't have to add data migrations in the future. --- app/models/sdg/goal.rb | 1 + app/models/sdg/local_target.rb | 10 ++++++++-- .../20210123100638_add_goals_to_local_targets.rb | 5 +++++ db/schema.rb | 4 +++- spec/factories/sdg.rb | 1 + spec/models/sdg/local_target_spec.rb | 11 ++++++++++- 6 files changed, 28 insertions(+), 4 deletions(-) create mode 100644 db/migrate/20210123100638_add_goals_to_local_targets.rb diff --git a/app/models/sdg/goal.rb b/app/models/sdg/goal.rb index c342dde35..167dc2bf4 100644 --- a/app/models/sdg/goal.rb +++ b/app/models/sdg/goal.rb @@ -4,6 +4,7 @@ class SDG::Goal < ApplicationRecord validates :code, presence: true, uniqueness: true, inclusion: { in: 1..17 } has_many :targets, dependent: :destroy + has_many :local_targets, dependent: :destroy def title I18n.t("sdg.goals.goal_#{code}.title") diff --git a/app/models/sdg/local_target.rb b/app/models/sdg/local_target.rb index 6711519d1..1bc9a9a0e 100644 --- a/app/models/sdg/local_target.rb +++ b/app/models/sdg/local_target.rb @@ -2,8 +2,6 @@ class SDG::LocalTarget < ApplicationRecord include Comparable include SDG::Related - delegate :goal, to: :target - translates :title, touch: true translates :description, touch: true include Globalizable @@ -14,8 +12,12 @@ class SDG::LocalTarget < ApplicationRecord validates :code, presence: true, uniqueness: true, format: ->(local_target) { /\A#{local_target.target&.code}\.\d+/ } validates :target, presence: true + validates :goal, presence: true belongs_to :target + belongs_to :goal + + before_validation :set_related_goal def self.[](code) find_by!(code: code) @@ -40,4 +42,8 @@ class SDG::LocalTarget < ApplicationRecord def subcode code.split(".").last end + + def set_related_goal + self.goal ||= target&.goal + end end diff --git a/db/migrate/20210123100638_add_goals_to_local_targets.rb b/db/migrate/20210123100638_add_goals_to_local_targets.rb new file mode 100644 index 000000000..b74846f3e --- /dev/null +++ b/db/migrate/20210123100638_add_goals_to_local_targets.rb @@ -0,0 +1,5 @@ +class AddGoalsToLocalTargets < ActiveRecord::Migration[5.2] + def change + add_reference :sdg_local_targets, :goal + end +end diff --git a/db/schema.rb b/db/schema.rb index c06db2e50..25e23d4cb 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2021_01_07_125458) do +ActiveRecord::Schema.define(version: 2021_01_23_100638) do # These are extensions that must be enabled in order to support this database enable_extension "pg_trgm" @@ -1322,7 +1322,9 @@ ActiveRecord::Schema.define(version: 2021_01_07_125458) do t.string "code" t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.bigint "goal_id" t.index ["code"], name: "index_sdg_local_targets_on_code", unique: true + t.index ["goal_id"], name: "index_sdg_local_targets_on_goal_id" t.index ["target_id"], name: "index_sdg_local_targets_on_target_id" end diff --git a/spec/factories/sdg.rb b/spec/factories/sdg.rb index 42073cf67..1f933eb04 100644 --- a/spec/factories/sdg.rb +++ b/spec/factories/sdg.rb @@ -13,6 +13,7 @@ FactoryBot.define do sequence(:description) { |n| "Help for Local Target #{n}" } target { SDG::Target[code.rpartition(".").first] } + goal { SDG::Goal[code.split(".")[0]] } end factory :sdg_phase, class: "SDG::Phase" do diff --git a/spec/models/sdg/local_target_spec.rb b/spec/models/sdg/local_target_spec.rb index 6bea11139..ecccc2a3d 100644 --- a/spec/models/sdg/local_target_spec.rb +++ b/spec/models/sdg/local_target_spec.rb @@ -18,7 +18,7 @@ describe SDG::LocalTarget do end it "is not valid without a code" do - expect(build(:sdg_local_target, code: nil, target: SDG::Target[1.1])).not_to be_valid + expect(build(:sdg_local_target, code: nil, target: SDG::Target[1.1], goal: SDG::Goal[1])).not_to be_valid end it "is not valid when code does not include associated target code" do @@ -47,6 +47,15 @@ describe SDG::LocalTarget do expect(build(:sdg_local_target, target: nil)).not_to be_valid end + describe "#set_related_goal" do + it "before validation set related goal" do + local_target = build(:sdg_local_target, code: "1.1.1", target: SDG::Target["1.1"], goal: nil) + + expect(local_target).to be_valid + expect(local_target.goal).to eq(SDG::Goal[1]) + end + end + describe "#goal" do it "returns the target goal" do local_target = create(:sdg_local_target, code: "1.1.1")