Skip to content

Commit 33ad849

Browse files
committed
Convert ElementDefinition into ActiveModel
Like with the PageDefinition model we want a strong contract
1 parent f47f8b3 commit 33ad849

26 files changed

Lines changed: 424 additions & 234 deletions

app/decorators/alchemy/element_editor.rb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,15 @@ def to_partial_path
1414
#
1515
# @return Array<Alchemy::IngredientEditor>
1616
def ingredients
17-
element.definition.fetch(:ingredients, []).map do |ingredient|
17+
ingredient_definitions.map do |ingredient|
1818
Alchemy::IngredientEditor.new(find_or_create_ingredient(ingredient))
1919
end
2020
end
2121

2222
# Are any ingredients defined?
2323
# @return [Boolean]
2424
def has_ingredients_defined?
25-
element.definition.fetch(:ingredients, []).any?
25+
ingredient_definitions.any?
2626
end
2727

2828
# Returns the translated ingredient group for displaying in admin editor group headings
@@ -108,9 +108,9 @@ def respond_to?(*args, **kwargs)
108108
# deprecated: This element will be removed soon.
109109
#
110110
def deprecation_notice
111-
case definition["deprecated"]
111+
case definition.deprecated
112112
when String
113-
definition["deprecated"]
113+
definition.deprecated
114114
when TrueClass
115115
Alchemy.t(
116116
name,

app/decorators/alchemy/ingredient_editor.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ def presence_validation?
173173
private
174174

175175
def form_field_counter
176-
element.definition.fetch(:ingredients, []).index { |i| i[:role] == role }
176+
element.ingredient_definitions.index { |i| i[:role] == role }
177177
end
178178
end
179179
end

app/helpers/alchemy/admin/elements_helper.rb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@ module ElementsHelper
1313
def elements_for_select(elements)
1414
return [] if elements.nil?
1515

16-
elements.collect do |e|
16+
elements.map do |e|
1717
[
18-
Element.display_name_for(e["name"]),
19-
e["name"]
18+
Element.display_name_for(e.name),
19+
e.name
2020
]
21-
end.sort
21+
end.tap(&:sort!)
2222
end
2323
end
2424
end

app/models/alchemy/element.rb

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ class Element < BaseRecord
3030

3131
include Alchemy::Logger
3232
include Alchemy::Taggable
33-
include Alchemy::Hints
3433

3534
FORBIDDEN_DEFINITION_ATTRIBUTES = [
3635
"amount",
@@ -107,6 +106,7 @@ class Element < BaseRecord
107106
scope :not_nested, -> { where(parent_element_id: nil) }
108107

109108
delegate :restricted?, to: :page, allow_nil: true
109+
delegate :has_hint?, :hint, to: :definition
110110

111111
# Concerns
112112
include Definitions
@@ -131,7 +131,7 @@ def new(attributes = {})
131131
raise(ElementDefinitionError, attributes)
132132
end
133133

134-
super(element_definition.merge(element_attributes).except(*FORBIDDEN_DEFINITION_ATTRIBUTES))
134+
super(element_definition.attributes.merge(element_attributes).except(*FORBIDDEN_DEFINITION_ATTRIBUTES))
135135
end
136136

137137
# This methods does a copy of source and all its ingredients.
@@ -167,7 +167,7 @@ def all_from_clipboard_for_page(clipboard, page)
167167
def all_from_clipboard_for_parent_element(clipboard, parent_element)
168168
return none if clipboard.nil? || parent_element.nil?
169169

170-
all_from_clipboard(clipboard).where(name: parent_element.definition["nestable_elements"])
170+
all_from_clipboard(clipboard).where(name: parent_element.definition.nestable_elements)
171171
end
172172
end
173173

@@ -211,7 +211,7 @@ def store_page(page)
211211

212212
# Returns true if the definition of this element has a taggable true value.
213213
def taggable?
214-
definition["taggable"] == true
214+
definition.taggable == true
215215
end
216216

217217
# The opposite of folded?
@@ -221,7 +221,7 @@ def expanded?
221221

222222
# Defined as compact element?
223223
def compact?
224-
definition["compact"] == true
224+
definition.compact
225225
end
226226

227227
# Defined as deprecated element?
@@ -253,7 +253,7 @@ def compact?
253253
#
254254
# @return Boolean
255255
def deprecated?
256-
!!definition["deprecated"]
256+
!!definition.deprecated
257257
end
258258

259259
# The element's view partial is dependent from its name
@@ -276,13 +276,13 @@ def to_partial_path
276276

277277
# A collection of element names that can be nested inside this element.
278278
def nestable_elements
279-
definition.fetch("nestable_elements", [])
279+
definition.nestable_elements
280280
end
281281

282282
private
283283

284284
def generate_nested_elements
285-
definition.fetch("autogenerate", []).each do |nestable_element|
285+
definition.autogenerate.each do |nestable_element|
286286
if nestable_elements.include?(nestable_element)
287287
Element.create(page_version: page_version, parent_element_id: id, name: nestable_element)
288288
else

app/models/alchemy/element/definitions.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ def definition
4646
else
4747
log_warning "Could not find element definition for #{name}. " \
4848
"Please check your elements.yml file!"
49-
{}
49+
ElementDefinition.new
5050
end
5151
end
5252
end

app/models/alchemy/element/element_ingredients.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ def copy_ingredients_to(element)
5353

5454
# Returns all element ingredient definitions from the +elements.yml+ file
5555
def ingredient_definitions
56-
definition.fetch(:ingredients, [])
56+
definition.ingredients
5757
end
5858

5959
# Returns the definition for given ingredient role

app/models/alchemy/element/presenters.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ def display_name_for(name)
3333
# @see Alchemy::Element::Presenters#display_name_for
3434
#
3535
def display_name
36-
self.class.display_name_for(definition["name"] || name)
36+
self.class.display_name_for(definition.name || name)
3737
end
3838

3939
# Returns a preview text for element.
Lines changed: 52 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,36 @@
22

33
module Alchemy
44
class ElementDefinition
5+
include ActiveModel::Model
6+
include ActiveModel::Attributes
7+
include Alchemy::Hints
8+
9+
extend ActiveModel::Translation
10+
11+
attribute :name, :string
12+
attribute :unique, :boolean, default: false
13+
attribute :amount, :integer, default: Float::INFINITY
14+
attribute :taggable, :boolean, default: false
15+
attribute :compact, :boolean, default: false
16+
attribute :fixed, :boolean, default: false
17+
attribute :ingredients, default: []
18+
attribute :nestable_elements, default: []
19+
attribute :autogenerate, default: []
20+
attribute :deprecated
21+
attribute :message
22+
attribute :warning
23+
attribute :hint
24+
25+
delegate :blank?, to: :name
26+
527
class << self
628
# Returns the definitions from elements.yml file.
729
#
830
# Place a +elements.yml+ file inside your apps +config/alchemy+ folder to define
931
# your own set of elements
1032
#
1133
def all
12-
@definitions ||= read_definitions_file.map(&:with_indifferent_access)
34+
@definitions ||= read_definitions_file.map { new(**_1) }
1335
end
1436

1537
# Add additional page definitions to collection.
@@ -23,23 +45,17 @@ def all
2345
# @param [Array || Hash]
2446
# You can pass a single element definition as Hash, or a collection of elements as Array.
2547
#
26-
def add(element)
48+
def add(definition)
2749
all
28-
if element.is_a?(Array)
29-
@definitions += element
30-
elsif element.is_a?(Hash)
31-
@definitions << element
32-
else
33-
raise TypeError
34-
end
50+
@definitions += Array.wrap(definition).map { new(**_1) }
3551
end
3652

3753
# Returns one element definition by given name.
3854
#
3955
def get(name)
40-
return {} if name.blank?
56+
return new if name.blank?
4157

42-
all.detect { |a| a["name"] == name }
58+
all.detect { _1.name.casecmp(name).zero? }
4359
end
4460

4561
def reset!
@@ -54,12 +70,20 @@ def definitions_file_path
5470

5571
private
5672

73+
def definitions_file
74+
File.read(definitions_file_path)
75+
end
76+
77+
def definitions_file_exist?
78+
File.exist?(definitions_file_path)
79+
end
80+
5781
# Reads the element definitions from +config/alchemy/elements.yml+.
5882
#
5983
def read_definitions_file
60-
if File.exist?(definitions_file_path)
84+
if definitions_file_exist?
6185
YAML.safe_load(
62-
ERB.new(File.read(definitions_file_path)).result,
86+
ERB.new(definitions_file).result,
6387
permitted_classes: YAML_PERMITTED_CLASSES,
6488
aliases: true
6589
) || []
@@ -69,5 +93,20 @@ def read_definitions_file
6993
end
7094
end
7195
end
96+
97+
def attributes
98+
super.with_indifferent_access
99+
end
100+
alias_method :definition, :attributes
101+
102+
def ingredients
103+
super.map(&:with_indifferent_access)
104+
end
105+
106+
private
107+
108+
def hint_translation_scope
109+
:element_hints
110+
end
72111
end
73112
end

app/models/alchemy/page/page_elements.rb

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ def duplicate_elements(elements, repository, page_version)
8282
def available_element_definitions(only_element_named = nil)
8383
@_available_element_definitions ||= if only_element_named
8484
element_definition = Element.definition_by_name(only_element_named)
85-
element_definitions_by_name(element_definition["nestable_elements"])
85+
element_definitions_by_name(element_definition.nestable_elements)
8686
else
8787
element_definitions.dup
8888
end
@@ -100,7 +100,7 @@ def available_element_definitions(only_element_named = nil)
100100
# All names of elements that can actually be placed on current page.
101101
#
102102
def available_element_names
103-
@_available_element_names ||= available_element_definitions.map { |e| e["name"] }
103+
@_available_element_names ||= available_element_definitions.map(&:name)
104104
end
105105

106106
# Available element definitions excluding nested unique elements.
@@ -109,7 +109,7 @@ def available_elements_within_current_scope(parent)
109109
@_available_elements = if parent
110110
parents_unique_nested_elements = parent.nested_elements.where(unique: true).pluck(:name)
111111
available_element_definitions(parent.name).reject do |e|
112-
parents_unique_nested_elements.include? e["name"]
112+
parents_unique_nested_elements.include?(e.name)
113113
end
114114
else
115115
available_element_definitions
@@ -129,10 +129,9 @@ def element_definitions
129129
#
130130
def descendent_element_definitions
131131
definitions = element_definitions_by_name(element_definition_names)
132-
definitions.select { |d| d.key?("nestable_elements") }.each do |d|
133-
definitions += element_definitions_by_name(d["nestable_elements"])
134-
end
135-
definitions.uniq { _1["name"] }
132+
definitions.select { _1.nestable_elements.any? }.flat_map do |d|
133+
element_definitions_by_name(d.nestable_elements)
134+
end.uniq(&:name)
136135
end
137136

138137
# All names of elements that are defined in the page definition.
@@ -161,7 +160,7 @@ def element_definitions_by_name(names)
161160
if names.to_s == "all"
162161
Element.definitions
163162
else
164-
Element.definitions.select { |e| names.include? e["name"] }
163+
Element.definitions.select { names.include?(_1.name) }
165164
end
166165
end
167166

@@ -190,16 +189,16 @@ def generate_elements
190189
#
191190
def delete_unique_element_definitions!
192191
@_available_element_definitions.delete_if do |element|
193-
element["unique"] && @_existing_element_names.include?(element["name"])
192+
element.unique && @_existing_element_names.include?(element.name)
194193
end
195194
end
196195

197196
# Deletes limited and outnumbered definitions from @_available_element_definitions.
198197
#
199198
def delete_outnumbered_element_definitions!
200199
@_available_element_definitions.delete_if do |element|
201-
outnumbered = @_existing_element_names.select { |name| name == element["name"] }
202-
element["amount"] && outnumbered.count >= element["amount"].to_i
200+
outnumbered = @_existing_element_names.select { |name| name == element.name }
201+
element.amount && outnumbered.count >= element.amount
203202
end
204203
end
205204
end

app/views/alchemy/admin/clipboard/_update_nested_element_button.turbo_stream.erb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<% if @item.class == Alchemy::Element %>
33
<%
44
@item.page.draft_version.elements.expanded.select do |element|
5-
element.definition["nestable_elements"] == [@item.name]
5+
element.nestable_elements == [@item.name]
66
end.each do |element|
77
%>
88
<%= turbo_stream.replace_all ".add-nested-element[data-element-id='#{element.id}']",

0 commit comments

Comments
 (0)