-
-
Notifications
You must be signed in to change notification settings - Fork 315
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add has_many ingredients relation to element
- Loading branch information
Showing
8 changed files
with
329 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
# frozen_string_literal: true | ||
|
||
module Alchemy | ||
class Element < BaseRecord | ||
# Methods concerning ingredients for elements | ||
# | ||
module ElementIngredients | ||
extend ActiveSupport::Concern | ||
|
||
included do | ||
attr_accessor :autogenerate_ingredients | ||
|
||
has_many :ingredients, | ||
class_name: "Alchemy::Ingredient", | ||
inverse_of: :element, | ||
dependent: :destroy | ||
|
||
before_create :build_ingredients, | ||
unless: -> { autogenerate_ingredients == false } | ||
end | ||
|
||
# Find first ingredient from element by given role. | ||
def ingredient_by_role(role) | ||
ingredients.detect { |ingredient| ingredient.role == role.to_s } | ||
end | ||
|
||
# Find first ingredient from element by given type. | ||
def ingredient_by_type(type) | ||
ingredients_by_type(type).first | ||
end | ||
|
||
# All ingredients from element by given type. | ||
def ingredients_by_type(type) | ||
ingredients.select do |ingredient| | ||
ingredient.type == Ingredient.normalize_type(type) | ||
end | ||
end | ||
|
||
# Copy current ingredient's ingredients to given target element | ||
def copy_ingredients_to(element) | ||
ingredients.map do |ingredient| | ||
Ingredient.copy(ingredient, element_id: element.id) | ||
end | ||
end | ||
|
||
# Returns all element ingredient definitions from the +elements.yml+ file | ||
def ingredient_definitions | ||
definition.fetch(:ingredients, []) | ||
end | ||
|
||
# Returns the definition for given ingredient role | ||
def ingredient_definition_for(role) | ||
if ingredient_definitions.blank? | ||
log_warning "Element #{name} is missing the ingredient definition for #{role}" | ||
nil | ||
else | ||
ingredient_definitions.find { |d| d[:role] == role.to_s } | ||
end | ||
end | ||
|
||
# Returns an array of all Richtext ingredients ids from elements | ||
# | ||
# This is used to re-initialize the TinyMCE editor in the element editor. | ||
# | ||
def richtext_ingredients_ids | ||
ids = ingredients.select(&:has_tinymce?).collect(&:id) | ||
expanded_nested_elements = nested_elements.expanded | ||
if expanded_nested_elements.present? | ||
ids += expanded_nested_elements.collect(&:richtext_ingredients_ids) | ||
end | ||
ids.flatten | ||
end | ||
|
||
# Has any of the ingredients validations defined? | ||
def has_validations? | ||
ingredients.any?(&:has_validations?) | ||
end | ||
|
||
# All element ingredients where the validation has failed. | ||
def ingredients_with_errors | ||
ingredients.select(&:validation_failed?) | ||
end | ||
|
||
# True if the element has a ingredient for given name | ||
# that has a non blank value. | ||
def has_value_for?(role) | ||
ingredient_by_role(role)&.value.present? | ||
end | ||
|
||
private | ||
|
||
# Builds ingredients for this element as described in the +elements.yml+ | ||
def build_ingredients | ||
self.ingredients = ingredient_definitions.map do |attributes| | ||
Ingredient.build(role: attributes[:role], element: self) | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
# frozen_string_literal: true | ||
|
||
require "rails_helper" | ||
|
||
RSpec.describe Alchemy::Element do | ||
it { is_expected.to have_many(:ingredients) } | ||
|
||
let(:element) { build(:alchemy_element, name: "element_with_ingredients") } | ||
|
||
it "creates ingredients after creation" do | ||
expect { | ||
element.save! | ||
}.to change { element.ingredients.count }.by(2) | ||
end | ||
|
||
describe "#ingredients_by_type" do | ||
let(:element) { create(:alchemy_element, :with_ingredients) } | ||
let(:expected_ingredients) { element.ingredients.texts } | ||
|
||
context "with namespaced essence type" do | ||
subject { element.ingredients_by_type("Alchemy::Text") } | ||
|
||
it { is_expected.not_to be_empty } | ||
|
||
it("should return the correct list of essences") { is_expected.to eq(expected_ingredients) } | ||
end | ||
|
||
context "without namespaced essence type" do | ||
subject { element.ingredients_by_type("Text") } | ||
|
||
it { is_expected.not_to be_empty } | ||
|
||
it("should return the correct list of essences") { is_expected.to eq(expected_ingredients) } | ||
end | ||
end | ||
|
||
describe "#ingredient_by_type" do | ||
let!(:element) { create(:alchemy_element, :with_ingredients) } | ||
let(:ingredient) { element.ingredients.first } | ||
|
||
context "with namespaced essence type" do | ||
it "should return ingredient by passing a essence type" do | ||
expect(element.ingredient_by_type("Alchemy::Text")).to eq(ingredient) | ||
end | ||
end | ||
|
||
context "without namespaced essence type" do | ||
it "should return ingredient by passing a essence type" do | ||
expect(element.ingredient_by_type("Text")).to eq(ingredient) | ||
end | ||
end | ||
end | ||
|
||
describe "#ingredient_by_role" do | ||
let!(:element) { create(:alchemy_element, :with_ingredients) } | ||
let(:ingredient) { element.ingredients.first } | ||
|
||
context "with role existing" do | ||
it "should return ingredient" do | ||
expect(element.ingredient_by_role(:headline)).to eq(ingredient) | ||
end | ||
end | ||
|
||
context "role not existing" do | ||
it { expect(element.ingredient_by_role(:foo)).to be_nil } | ||
end | ||
end | ||
|
||
describe "#has_value_for?" do | ||
let!(:element) { create(:alchemy_element, :with_ingredients) } | ||
|
||
context "with role existing" do | ||
let(:ingredient) { element.ingredients.first } | ||
|
||
context "with blank value" do | ||
before do | ||
expect(ingredient).to receive(:value) { nil } | ||
end | ||
|
||
it { expect(element.has_value_for?(:headline)).to be(false) } | ||
end | ||
|
||
context "with value present" do | ||
before do | ||
expect(ingredient).to receive(:value) { "Headline" } | ||
end | ||
|
||
it "should return ingredient" do | ||
expect(element.has_value_for?(:headline)).to be(true) | ||
end | ||
end | ||
end | ||
|
||
context "role not existing" do | ||
it { expect(element.has_value_for?(:foo)).to be(false) } | ||
end | ||
end | ||
end |
Oops, something went wrong.